<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.14.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":-1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta property="og:type" content="website">
<meta property="og:title" content="JsyBlog">
<meta property="og:url" content="http://example.com/page/15/index.html">
<meta property="og:site_name" content="JsyBlog">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="SongyangJi">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://example.com/page/15/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/15/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>JsyBlog</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">JsyBlog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">SongyangJi</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">251</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">45</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">109</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/15/CopyOnWriteArrayList%E5%92%8CCopyOnWriteArraySet/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/15/CopyOnWriteArrayList%E5%92%8CCopyOnWriteArraySet/" class="post-title-link" itemprop="url">CopyOnWriteArrayList和CopyOnWriteArraySet</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-15 00:34:34" itemprop="dateCreated datePublished" datetime="2021-12-15T00:34:34+08:00">2021-12-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-04 22:30:28" itemprop="dateModified" datetime="2023-01-04T22:30:28+08:00">2023-01-04</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JUC/" itemprop="url" rel="index"><span itemprop="name">JUC</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="ArrayList的线程不安全性"><a href="#ArrayList的线程不安全性" class="headerlink" title="ArrayList的线程不安全性"></a>ArrayList的线程不安全性</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.Vector;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.CopyOnWriteArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.TimeUnit;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test</span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line"><span class="comment">//        List&lt;Integer&gt; list = new ArrayList&lt;&gt;();</span></span><br><span class="line">        List&lt;Integer&gt; list = <span class="keyword">new</span> <span class="title class_">CopyOnWriteArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">        list.add(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">           list.set(<span class="number">0</span>,<span class="number">999</span>);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="comment">// 数据没有更改，一直循环等待下去</span></span><br><span class="line">            <span class="keyword">while</span> (list.get(<span class="number">0</span>) == <span class="number">0</span>) &#123;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;list中的元素被修改为： &quot;</span>+list.get(<span class="number">0</span>));</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        thread2.start();</span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">3</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;修改list中的元素&quot;</span>);</span><br><span class="line">        thread1.start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h1 id="CopyOnWrite"><a href="#CopyOnWrite" class="headerlink" title="CopyOnWrite"></a>CopyOnWrite</h1><p>ArrayList的线程安全变体。</p>
<p><strong>所有可变操作（ add 、 set等）都是通过制作底层数组的新副本来实现的</strong>。</p>
<p>这通常成本太高，但当遍历操作大大超过突变时可能比替代方法更有效，并且当您不能或不想同步遍历时很有用，但需要排除并发线程之间的干扰。</p>
<p> “快照”样式的迭代器方法使用对创建迭代器时数组状态的引用。 </p>
<p>这个数组在迭代器的生命周期内永远不会改变，所以线程之间的干扰不会发生，并且迭代器保证不会抛出<code>ConcurrentModificationException</code> 。</p>
<p> 自创建迭代器以来，迭代器不会反映对列表的添加、删除或更改。 </p>
<p>不支持迭代器本身的元素更改操作（ remove 、 set和add ）， 这些方法会抛<code>UnsupportedOperationException </code>。</p>
<p>允许 null 元素。</p>
<blockquote>
<p>RCU(Read-Copy Update)，是Linux 中比较重要的一种同步机制。 顾名思义就是“读，拷贝更新”，再直白点是“随意读，但更新数据的时候，需要先复制一份副本，在副本上完成修改，再一次性地替换旧数据”。 这是Linux 内核实现的一种针对“读多写少”的共享数据的同步机制。</p>
</blockquote>
<h2 id="关于set方法的几点疑问"><a href="#关于set方法的几点疑问" class="headerlink" title="关于set方法的几点疑问"></a>关于set方法的几点疑问</h2><h2 id="源码解析"><a href="#源码解析" class="headerlink" title="源码解析"></a>源码解析</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> java.util.concurrent;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CopyOnWriteArrayList</span>&lt;E&gt;</span><br><span class="line">    <span class="keyword">implements</span> <span class="title class_">List</span>&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> <span class="number">8673264195747942595L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** The lock protecting all mutators */</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">transient</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** The array, accessed only via getArray/setArray. */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> Object[] array;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">		</span><br><span class="line">    <span class="comment">// 读不加任何锁，直接读</span></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> E <span class="title function_">get</span><span class="params">(Object[] a, <span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (E) a[index];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 1. 加锁</span></span><br><span class="line">    <span class="comment">// 2. 写时复制 (拷贝原数组, 更改元素)</span></span><br><span class="line">    <span class="comment">// 3. 引用新数组</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">set</span><span class="params">(<span class="type">int</span> index, E element)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="built_in">this</span>.lock;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Object[] elements = getArray();</span><br><span class="line">            <span class="type">E</span> <span class="variable">oldValue</span> <span class="operator">=</span> get(elements, index);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (oldValue != element) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> elements.length;</span><br><span class="line">                Object[] newElements = Arrays.copyOf(elements, len);</span><br><span class="line">                newElements[index] = element;</span><br><span class="line">                setArray(newElements);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// Not quite a no-op; ensures volatile write semantics</span></span><br><span class="line">                setArray(elements); <span class="comment">// 确保写volatile的写语义</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> oldValue;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="comment">// 1. 加锁</span></span><br><span class="line">    <span class="comment">// 2. 写时复制 (拷贝原数组, 添加新元素)</span></span><br><span class="line">    <span class="comment">// 3. 引用新数组</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">add</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="built_in">this</span>.lock;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Object[] elements = getArray();</span><br><span class="line">            <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> elements.length;</span><br><span class="line">            Object[] newElements = Arrays.copyOf(elements, len + <span class="number">1</span>);</span><br><span class="line">            newElements[len] = e;</span><br><span class="line">            setArray(newElements);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 与上面的做法如出一辙</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> index, E element)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="built_in">this</span>.lock;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Object[] elements = getArray();</span><br><span class="line">            <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> elements.length;</span><br><span class="line">            <span class="keyword">if</span> (index &gt; len || index &lt; <span class="number">0</span>)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IndexOutOfBoundsException</span>(<span class="string">&quot;Index: &quot;</span>+index+</span><br><span class="line">                                                    <span class="string">&quot;, Size: &quot;</span>+len);</span><br><span class="line">            Object[] newElements;</span><br><span class="line">            <span class="type">int</span> <span class="variable">numMoved</span> <span class="operator">=</span> len - index;</span><br><span class="line">            <span class="keyword">if</span> (numMoved == <span class="number">0</span>)</span><br><span class="line">                newElements = Arrays.copyOf(elements, len + <span class="number">1</span>);</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                newElements = <span class="keyword">new</span> <span class="title class_">Object</span>[len + <span class="number">1</span>];</span><br><span class="line">                System.arraycopy(elements, <span class="number">0</span>, newElements, <span class="number">0</span>, index);</span><br><span class="line">                System.arraycopy(elements, index, newElements, index + <span class="number">1</span>,</span><br><span class="line">                                 numMoved);</span><br><span class="line">            &#125;</span><br><span class="line">            newElements[index] = element;</span><br><span class="line">            setArray(newElements);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 和 add 操作是一样的，不过是一个增加元素，一个是删除元素没什么区别</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">remove</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="built_in">this</span>.lock;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Object[] elements = getArray();</span><br><span class="line">            <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> elements.length;</span><br><span class="line">            <span class="type">E</span> <span class="variable">oldValue</span> <span class="operator">=</span> get(elements, index);</span><br><span class="line">            <span class="type">int</span> <span class="variable">numMoved</span> <span class="operator">=</span> len - index - <span class="number">1</span>;</span><br><span class="line">            <span class="keyword">if</span> (numMoved == <span class="number">0</span>)</span><br><span class="line">                setArray(Arrays.copyOf(elements, len - <span class="number">1</span>));</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                Object[] newElements = <span class="keyword">new</span> <span class="title class_">Object</span>[len - <span class="number">1</span>];</span><br><span class="line">                System.arraycopy(elements, <span class="number">0</span>, newElements, <span class="number">0</span>, index);</span><br><span class="line">                System.arraycopy(elements, index + <span class="number">1</span>, newElements, index,</span><br><span class="line">                                 numMoved);</span><br><span class="line">                setArray(newElements);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> oldValue;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 不要立即加锁（当然这样并不错，这可能没必要加锁，因为有可能要删除的对象可能压根不在数组里）</span></span><br><span class="line">    <span class="comment">// 所以先拿到快照去检查一下看有没有，没有的话直接返回false，有的话才去删除</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">remove</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">        Object[] snapshot = getArray(); </span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> indexOf(o, snapshot, <span class="number">0</span>, snapshot.length);</span><br><span class="line">        <span class="keyword">return</span> (index &lt; <span class="number">0</span>) ? <span class="literal">false</span> : remove(o, snapshot, index);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">remove</span><span class="params">(Object o, Object[] snapshot, <span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="built_in">this</span>.lock;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Object[] current = getArray();</span><br><span class="line">            <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> current.length;</span><br><span class="line">            <span class="comment">// 如果在争抢到锁的时候，数组已经被修改了，也就是说此时 snapshot != current,</span></span><br><span class="line">            <span class="comment">// 还需要再次检查，寻找要删除的index</span></span><br><span class="line">            <span class="keyword">if</span> (snapshot != current) findIndex: &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">prefix</span> <span class="operator">=</span> Math.min(index, len);</span><br><span class="line">                <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; prefix; i++) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (current[i] != snapshot[i] &amp;&amp; eq(o, current[i])) &#123;</span><br><span class="line">                        index = i; <span class="comment">// 找到了</span></span><br><span class="line">                        <span class="keyword">break</span> findIndex;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (index &gt;= len) <span class="comment">// 没找到</span></span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">                <span class="keyword">if</span> (current[index] == o)</span><br><span class="line">                    <span class="keyword">break</span> findIndex;</span><br><span class="line">                index = indexOf(o, current, index, len);</span><br><span class="line">                <span class="keyword">if</span> (index &lt; <span class="number">0</span>) <span class="comment">// 没找到</span></span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果说快照仍然保持为最新的话，其实和上面的remove(index)的处理逻辑是一样的</span></span><br><span class="line">            Object[] newElements = <span class="keyword">new</span> <span class="title class_">Object</span>[len - <span class="number">1</span>];</span><br><span class="line">            System.arraycopy(current, <span class="number">0</span>, newElements, <span class="number">0</span>, index);</span><br><span class="line">            System.arraycopy(current, index + <span class="number">1</span>,</span><br><span class="line">                             newElements, index,</span><br><span class="line">                             len - index - <span class="number">1</span>);</span><br><span class="line">            setArray(newElements);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   	<span class="comment">// (节省篇幅，代码省略了)</span></span><br><span class="line">		<span class="comment">// clear、sort 和上面也是一样，先加锁，对快照进行修改，最后引用新数组 		</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">clear</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sort</span><span class="params">(Comparator&lt;? <span class="built_in">super</span> E&gt; c)</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>迭代器</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Iterator&lt;E&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">COWIterator</span>&lt;E&gt;(getArray(), <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">COWIterator</span>&lt;E&gt; <span class="keyword">implements</span> <span class="title class_">ListIterator</span>&lt;E&gt; &#123;</span><br><span class="line">    <span class="comment">/** Snapshot of the array */</span></span><br><span class="line">    <span class="comment">// 拿到快照：自创建迭代器以来，迭代器不会反映对列表的添加、删除或更改。 </span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Object[] snapshot;</span><br><span class="line">    <span class="comment">/** Index of element to be returned by subsequent call to next.  */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> cursor;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">COWIterator</span><span class="params">(Object[] elements, <span class="type">int</span> initialCursor)</span> &#123;</span><br><span class="line">        cursor = initialCursor;</span><br><span class="line">        snapshot = elements;</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h1 id="Vector"><a href="#Vector" class="headerlink" title="Vector"></a>Vector</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Vector</span>&lt;E&gt;</span><br><span class="line">    <span class="keyword">extends</span> <span class="title class_">AbstractList</span>&lt;E&gt;</span><br><span class="line">    <span class="keyword">implements</span> <span class="title class_">List</span>&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable</span><br><span class="line">&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 无需 volatile 修饰，因为 synchronized 的内存语义可以做到</span></span><br><span class="line">    <span class="keyword">protected</span> Object[] elementData;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="type">int</span> elementCount;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> elementCount == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="type">int</span> <span class="title function_">indexOf</span><span class="params">(Object o, <span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (o == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> index ; i &lt; elementCount ; i++)</span><br><span class="line">                <span class="keyword">if</span> (elementData[i]==<span class="literal">null</span>)</span><br><span class="line">                    <span class="keyword">return</span> i;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> index ; i &lt; elementCount ; i++)</span><br><span class="line">                <span class="keyword">if</span> (o.equals(elementData[i]))</span><br><span class="line">                    <span class="keyword">return</span> i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> E <span class="title function_">get</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (index &gt;= elementCount)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArrayIndexOutOfBoundsException</span>(index);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> elementData(index);</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> E <span class="title function_">set</span><span class="params">(<span class="type">int</span> index, E element)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (index &gt;= elementCount)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArrayIndexOutOfBoundsException</span>(index);</span><br><span class="line"></span><br><span class="line">        <span class="type">E</span> <span class="variable">oldValue</span> <span class="operator">=</span> elementData(index);</span><br><span class="line">        elementData[index] = element;</span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="type">boolean</span> <span class="title function_">add</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        modCount++;</span><br><span class="line">        ensureCapacityHelper(elementCount + <span class="number">1</span>);</span><br><span class="line">        elementData[elementCount++] = e;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">synchronized</span> E <span class="title function_">remove</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        modCount++;</span><br><span class="line">        <span class="keyword">if</span> (index &gt;= elementCount)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ArrayIndexOutOfBoundsException</span>(index);</span><br><span class="line">        <span class="type">E</span> <span class="variable">oldValue</span> <span class="operator">=</span> elementData(index);</span><br><span class="line"></span><br><span class="line">        <span class="type">int</span> <span class="variable">numMoved</span> <span class="operator">=</span> elementCount - index - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (numMoved &gt; <span class="number">0</span>)</span><br><span class="line">            System.arraycopy(elementData, index+<span class="number">1</span>, elementData, index,</span><br><span class="line">                             numMoved);</span><br><span class="line">        elementData[--elementCount] = <span class="literal">null</span>; <span class="comment">// Let gc do its work</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h1 id="SynchronizedList"><a href="#SynchronizedList" class="headerlink" title="SynchronizedList"></a>SynchronizedList</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SynchronizedList</span>&lt;E&gt;</span><br><span class="line">    <span class="keyword">extends</span> <span class="title class_">SynchronizedCollection</span>&lt;E&gt;</span><br><span class="line">    <span class="keyword">implements</span> <span class="title class_">List</span>&lt;E&gt; &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> -<span class="number">7754090372962971524L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">final</span> List&lt;E&gt; list;</span><br><span class="line"></span><br><span class="line">    SynchronizedList(List&lt;E&gt; list) &#123;</span><br><span class="line">        <span class="built_in">super</span>(list);</span><br><span class="line">        <span class="built_in">this</span>.list = list;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 下面代码中出现的 mutex 都是继承父类 </span></span><br><span class="line"><span class="comment">     * SynchronizedCollection 中的监视器对象 mutex</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    SynchronizedList(List&lt;E&gt; list, Object mutex) &#123;</span><br><span class="line">        <span class="built_in">super</span>(list, mutex);</span><br><span class="line">        <span class="built_in">this</span>.list = list;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">equals</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span> == o)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> list.equals(o);&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">hashCode</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> list.hashCode();&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">get</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> list.get(index);&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">set</span><span class="params">(<span class="type">int</span> index, E element)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> list.set(index, element);&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> index, E element)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;list.add(index, element);&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">remove</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> list.remove(index);&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">indexOf</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> list.indexOf(o);&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">lastIndexOf</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> list.lastIndexOf(o);&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">addAll</span><span class="params">(<span class="type">int</span> index, Collection&lt;? extends E&gt; c)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> list.addAll(index, c);&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> ListIterator&lt;E&gt; <span class="title function_">listIterator</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> list.listIterator(); <span class="comment">// Must be manually synched by user</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> ListIterator&lt;E&gt; <span class="title function_">listIterator</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> list.listIterator(index); <span class="comment">// Must be manually synched by user</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> List&lt;E&gt; <span class="title function_">subList</span><span class="params">(<span class="type">int</span> fromIndex, <span class="type">int</span> toIndex)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SynchronizedList</span>&lt;&gt;(list.subList(fromIndex, toIndex),</span><br><span class="line">                                        mutex);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">replaceAll</span><span class="params">(UnaryOperator&lt;E&gt; operator)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;list.replaceAll(operator);&#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sort</span><span class="params">(Comparator&lt;? <span class="built_in">super</span> E&gt; c)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (mutex) &#123;list.sort(c);&#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h1 id="CopyOnWriteArraySet"><a href="#CopyOnWriteArraySet" class="headerlink" title="CopyOnWriteArraySet"></a>CopyOnWriteArraySet</h1><p>使用内部CopyOnWriteArrayList进行所有操作的Set 。 因此，它具有相同的基本属性：<br>它最适合集合大小通常保持较小、只读操作的数量远远超过可变操作的应用程序，并且您需要在遍历过程中防止线程之间的干扰。<br>它是线程安全的。<br>可变操作（ add 、 set 、 remove等）的开销很大，因为它们通常需要复制整个底层数组。<br>迭代器不支持可变remove操作。<br>通过迭代器遍历速度快，不会遇到来自其他线程的干扰。</p>
<p>迭代器依赖于构建迭代器时数组的不变快照。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CopyOnWriteArraySet</span>&lt;E&gt; <span class="keyword">extends</span> <span class="title class_">AbstractSet</span>&lt;E&gt;</span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">java</span>.io.Serializable &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> <span class="number">5457747651344034263L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 以此为基本实现容器</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> CopyOnWriteArrayList&lt;E&gt; al;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">CopyOnWriteArraySet</span><span class="params">()</span> &#123;</span><br><span class="line">        al = <span class="keyword">new</span> <span class="title class_">CopyOnWriteArrayList</span>&lt;E&gt;();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> al.size();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> al.isEmpty();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 注意这个时间复杂度是O(n)的</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">contains</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> al.contains(o);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// 注意这个时间复杂度是O(n)的</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">remove</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> al.remove(o);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// 注意这个时间复杂度是O(n)的</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">add</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> al.addIfAbsent(e);</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/15/ConcurrentLinkedQueue%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/15/ConcurrentLinkedQueue%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/" class="post-title-link" itemprop="url">ConcurrentLinkedQueue源码分析</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-12-15 00:24:43 / 修改时间：00:33:36" itemprop="dateCreated datePublished" datetime="2021-12-15T00:24:43+08:00">2021-12-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JUC/" itemprop="url" rel="index"><span itemprop="name">JUC</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p><code>ConcurrentLinkedQueue</code>，<strong>基于链表的无界的线程安全队列</strong>。</p>
<p> 当许多线程将共享对公共集合的访问时， ConcurrentLinkedQueue是合适的选择。 <strong>像大多数其他并发集合实现一样，此类不允许使用null元素</strong>。</p>
<p>此实现采用了一种高效的非阻塞算法，该算法基于 Maged M. Michael 和 Michael L. Scott 在《Simple、Fast、Practical Non-Blocking and Blocking Concurrent Queue Algorithms》 中描述的算法 。</p>
<p><strong>迭代器是弱一致性的，返回元素反映了在迭代器创建时或之后的某个时刻的队列状态</strong>。 </p>
<p>它们不会抛出 <code>java.util.ConcurrentModificationException</code> ，并且可能与其他操作同时进行。</p>
<p>自迭代器创建以来队列中包含的元素将只返回一次。</p>
<p>某个迭代器创建之后，使用它遍历队列元素时，队列中的某个元素正好返回一次。</p>
<p>请注意，与大多数集合不同， size方法不是时间复杂度为O(1)的操作。</p>
<p> 由于这些队列的异步性质，确定当前元素数量需要遍历元素（所以它的时间复杂度是O(n) ，需要我们注意），因此如果在遍历期间修改此集合，则可能会报告不准确的结果。</p>
<p>此外，批量操作addAll 、 removeAll 、 retainAll 、 containsAll 、 equals和toArray不能保证以原子方式执行。 </p>
<p>内存一致性影响：与其他并发集合一样，在一个线程中将对象放入ConcurrentLinkedQueue中的操作发生在另一个线程中从ConcurrentLinkedQueue访问或删除该元素之后的操作之前。</p>
<p>请注意，与此包中的大多数非阻塞算法一样，此实现依赖于以下事实：<br>在垃圾中收集到的系统中，不存在ABA问题的可能性。<br>要回收节点，则无需使用“计数指针”或其他相关技术。</p>
<p>基本的循环不变量:</p>
<ul>
<li>有且仅有一个节点的后即指针为null，就是队列的最后一个节点</li>
<li>CAS一个节点的引用和null引用，可以原子性地将它从队列中移除。</li>
<li>从head节点出发遍历所有元素的可达性必须保持，即使在并发修改的情况下。 </li>
<li>一个已经出队的节点仍然可能处于可使用状态，比如在发生<strong>创建一个迭代器</strong>或者<strong>使用poll</strong>(不过它暂时失去了cpu时间片)的情况时。</li>
</ul>
<p>一个优化，每当当前指针距离头&#x2F;尾指针两步远时候，都会去更新 head &#x2F; tail。</p>
<p>因为头尾节点都是并发并且独立地更新的，所以完全由可能一方落后于另一方更新。</p>
<p>头部和尾部都有可能滞后，</p>
<p>迭代器会跳过元素为null的节点。</p>
<p>当创造一个节点（入队操作前）的时候，为了避免 volatile 写的开销，</p>
<p>使用了 Unsafe.putObject 代替了普通写，是的排队的开销降为约1.5倍的CAS。</p>
<p>头部和尾部节点有可能引用，也有可能不引用一个有着非空项的节点。</p>
<p>如果队列是空的，那么所有元素必然都是空的。</p>
<p>创建节点时，头尾节点引用一个有着空元素的哑结点。</p>
<p>它们都使用CAS进行更新，所以永不倒回。</p>
<p>注：为了节省篇幅，略去部分方法以及注释。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> java.util.concurrent;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConcurrentLinkedQueue</span>&lt;E&gt; <span class="keyword">extends</span> <span class="title class_">AbstractQueue</span>&lt;E&gt;</span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">Queue</span>&lt;E&gt;, java.io.Serializable &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> <span class="number">196745693267521676L</span>;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;E&gt; &#123;</span><br><span class="line">        <span class="keyword">volatile</span> E item;</span><br><span class="line">        <span class="keyword">volatile</span> Node&lt;E&gt; next;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Constructs a new node.  Uses relaxed write because item can</span></span><br><span class="line"><span class="comment">         * only be seen after publication via casNext.</span></span><br><span class="line"><span class="comment">         * 创建一个节点，使用unsafe的方式，不过在使用 casNext 后才能真正遍历到它。</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        Node(E item) &#123;</span><br><span class="line">            UNSAFE.putObject(<span class="built_in">this</span>, itemOffset, item);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">boolean</span> <span class="title function_">casItem</span><span class="params">(E cmp, E val)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> UNSAFE.compareAndSwapObject(<span class="built_in">this</span>, itemOffset, cmp, val);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">      	<span class="comment">//  t.lazySetNext(newNode) &lt;==&gt; t.next = newNode</span></span><br><span class="line">        <span class="keyword">void</span> <span class="title function_">lazySetNext</span><span class="params">(Node&lt;E&gt; val)</span> &#123;</span><br><span class="line">            UNSAFE.putOrderedObject(<span class="built_in">this</span>, nextOffset, val); </span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">boolean</span> <span class="title function_">casNext</span><span class="params">(Node&lt;E&gt; cmp, Node&lt;E&gt; val)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> UNSAFE.compareAndSwapObject(<span class="built_in">this</span>, nextOffset, cmp, val);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// Unsafe mechanics</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> sun.misc.Unsafe UNSAFE;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> itemOffset;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> nextOffset;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">static</span> &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                UNSAFE = sun.misc.Unsafe.getUnsafe();</span><br><span class="line">                Class&lt;?&gt; k = Node.class;</span><br><span class="line">                itemOffset = UNSAFE.objectFieldOffset</span><br><span class="line">                    (k.getDeclaredField(<span class="string">&quot;item&quot;</span>));</span><br><span class="line">                nextOffset = UNSAFE.objectFieldOffset</span><br><span class="line">                    (k.getDeclaredField(<span class="string">&quot;next&quot;</span>));</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(e);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 不变量:</span></span><br><span class="line"><span class="comment">     * - 所有存活节点都可以通过 head 节点到达，通过后继指针 </span></span><br><span class="line"><span class="comment">     * - head != null</span></span><br><span class="line"><span class="comment">     * - (tmp = head).next != tmp || tmp != head  ???</span></span><br><span class="line"><span class="comment">     * 非不变量:</span></span><br><span class="line"><span class="comment">     * - head.item 可能为null，也有可能不为null,</span></span><br><span class="line"><span class="comment">		 * - 允许 tail 到 head的滞后，也就是可能无法从 head 到达 tail.</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> Node&lt;E&gt; head;</span><br><span class="line"></span><br><span class="line">   <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 不变量:</span></span><br><span class="line"><span class="comment">     * - the last node 总是可以通过 tail 节点到达，通过后继指针 （the last node 是唯一的其next值为 null 的 node）</span></span><br><span class="line"><span class="comment">     * - tail != null</span></span><br><span class="line"><span class="comment">     * 非不变量:</span></span><br><span class="line"><span class="comment">     * - tail.item 可能为null，也有可能不为null,</span></span><br><span class="line"><span class="comment">		 * - 允许 tail 到 head的滞后，也就是可能无法从 head 到达 tail.</span></span><br><span class="line"><span class="comment">     * - tail.next 可能也可能不”自我指向“ tail.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> Node&lt;E&gt; tail;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 空队列，head、tail 指向同一哑结点</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ConcurrentLinkedQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        head = tail = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;E&gt;(<span class="literal">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ConcurrentLinkedQueue</span><span class="params">(Collection&lt;? extends E&gt; c)</span> &#123;</span><br><span class="line">        Node&lt;E&gt; h = <span class="literal">null</span>, t = <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">for</span> (E e : c) &#123;</span><br><span class="line">            checkNotNull(e); <span class="comment">// 注意 ConcurrentLinkedQueue 不允许null元素</span></span><br><span class="line">            Node&lt;E&gt; newNode = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;E&gt;(e);</span><br><span class="line">            <span class="keyword">if</span> (h == <span class="literal">null</span>) <span class="comment">// 遇到第一个元素</span></span><br><span class="line">                h = t = newNode; </span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                t.lazySetNext(newNode); <span class="comment">// 相当于 t.next = newNode, 不过避免了 volatile 写</span></span><br><span class="line">                t = newNode;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (h == <span class="literal">null</span>) <span class="comment">// c 为 empty，同上</span></span><br><span class="line">            h = t = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;E&gt;(<span class="literal">null</span>);</span><br><span class="line">        head = h;</span><br><span class="line">        tail = t;</span><br><span class="line">    &#125;</span><br><span class="line"> </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Tries to CAS head to p. If successful, repoint old head to itself</span></span><br><span class="line"><span class="comment">     * as sentinel for succ(), below.</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     * CAS替换掉旧 head （h）为 新 head（p）</span></span><br><span class="line"><span class="comment">     * 如果替换成功，旧 head的next指针指向自己，成为哨兵</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">updateHead</span><span class="params">(Node&lt;E&gt; h, Node&lt;E&gt; p)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (h != p &amp;&amp; casHead(h, p))</span><br><span class="line">            h.lazySetNext(h); <span class="comment">// 使旧head的后继为head自己，起哨兵作用</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Returns the successor of p, or the head node if p.next has been</span></span><br><span class="line"><span class="comment">     * linked to self, which will only be true if traversing with a</span></span><br><span class="line"><span class="comment">     * stale pointer that is now off the list.</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     * 获取 p 的后继，不过如果 p.next == p 的话，说明 p 已经出队了</span></span><br><span class="line"><span class="comment">     *（p.next == p 就是我们设置的标志，见h.lazySetNext(h)，</span></span><br><span class="line"><span class="comment">     * 因为正常情况下不会出现这种情况 —— 正常情况下 p.next == p 说明链表成环） </span></span><br><span class="line"><span class="comment">     * 此时返回链表新的 head，表示从新头出发</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> Node&lt;E&gt; <span class="title function_">succ</span><span class="params">(Node&lt;E&gt; p)</span> &#123;</span><br><span class="line">        Node&lt;E&gt; next = p.next;</span><br><span class="line">        <span class="keyword">return</span> (p == next) ? head : next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Inserts the specified element at the tail of this queue.</span></span><br><span class="line"><span class="comment">     * As the queue is unbounded, this method will never return &#123;<span class="doctag">@code</span> false&#125;.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> &#123;<span class="doctag">@code</span> true&#125; (as specified by &#123;<span class="doctag">@link</span> Queue#offer&#125;)</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@throws</span> NullPointerException if the specified element is null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        checkNotNull(e);</span><br><span class="line">        <span class="keyword">final</span> Node&lt;E&gt; newNode = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;E&gt;(e);</span><br><span class="line"></span><br><span class="line">      	<span class="comment">// 无界队列，一定可以插入队尾成功。所以，如果失败，一直重试下去</span></span><br><span class="line">        <span class="keyword">for</span> (Node&lt;E&gt; t = tail, p = t;;) &#123;</span><br><span class="line">            Node&lt;E&gt; q = p.next;</span><br><span class="line">            <span class="keyword">if</span> (q == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// p is last node</span></span><br><span class="line">                <span class="keyword">if</span> (p.casNext(<span class="literal">null</span>, newNode)) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (p != t) <span class="comment">// p == t的时候不更新（防止每一次入队操作都去更新tail，间隔一次）</span></span><br><span class="line">                        casTail(t, newNode);  <span class="comment">// 失败也ok</span></span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// CAS竞争失败，下次再读，再去尝试</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (p == q) <span class="comment">// 只有一种情况出现 p == q, 就是 p 这个节点作为旧 head 从队列中被剔除出去，用 p.next = p 标识</span></span><br><span class="line">              </span><br><span class="line">                <span class="comment">// We have fallen off list.  If tail is unchanged, it</span></span><br><span class="line">                <span class="comment">// will also be off-list, in which case we need to</span></span><br><span class="line">                <span class="comment">// jump to head, from which all live nodes are always</span></span><br><span class="line">                <span class="comment">// reachable.  Else the new tail is a better bet.</span></span><br><span class="line">              	<span class="comment">// -------- 以上为源码注释 ---------</span></span><br><span class="line">                p = (t != (t = tail)) ? t : head; </span><br><span class="line">                <span class="comment">// 首先，跳到 head 处肯定是正确的，无非可能要多做几次循环找到尾节点罢了。</span></span><br><span class="line">                <span class="comment">// 其次，如果 tail 发生了变化，说明有其他线程入队了节点，并且成功修改了 tail，这个时候没必要从 head 出发,</span></span><br><span class="line">                <span class="comment">// 新的 tail 是更好的选择。</span></span><br><span class="line">          		  <span class="comment">// </span></span><br><span class="line">                <span class="comment">// 需要特别指明的是，如果 p 都已经出队了，那么 t 肯定也出队了（因为 p 是从 t 出发的嘛）</span></span><br><span class="line">                <span class="comment">// 如果说此时 tail 还没有发生变化，说明此时 tail 已经跑到了 head的前面（这是完全可能发生的）</span></span><br><span class="line">                <span class="comment">// 这时就必须跳到head了重新出发了。</span></span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                <span class="comment">// 寻找真正的”尾节点“</span></span><br><span class="line">                p = (p != t &amp;&amp; t != (t = tail)) ? t : q; </span><br><span class="line">                <span class="comment">/* </span></span><br><span class="line"><span class="comment">                 * 这个三元表达式，做了很多事，有判断，有赋值。</span></span><br><span class="line"><span class="comment">                 * 如果，改写成下面这样，行不行呢？</span></span><br><span class="line"><span class="comment">                 * t = tail; p = q;</span></span><br><span class="line"><span class="comment">                 * 我觉得也是没问题的，因为这是个LIFO的队列，p 不是 the last node，顺着往下找就是了.</span></span><br><span class="line"><span class="comment">                 * 不过作者认为，如果经过一次循环后（也就是 p 跳过一个节点之后,此时 p != t，如果又 tail发生了变化，</span></span><br><span class="line"><span class="comment">                 * 直接跳到新的 tail 处继续寻找更好）</span></span><br><span class="line"><span class="comment">                 */</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * </span></span><br><span class="line"><span class="comment">     * 这段逻辑和上面的offer（入队）差不多：</span></span><br><span class="line"><span class="comment">     * 1. 找到链表的真正的头结点（从 head 出发，找到第一个 item 不为null的节点）</span></span><br><span class="line"><span class="comment">     * 2. 将头结点出队（cas将item域置null）</span></span><br><span class="line"><span class="comment">     * 3. 更新 head （cas更新head，旧head标记为&quot;哨兵“）</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        restartFromHead:</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="keyword">for</span> (Node&lt;E&gt; h = head, p = h, q;;) &#123;</span><br><span class="line">                <span class="type">E</span> <span class="variable">item</span> <span class="operator">=</span> p.item;</span><br><span class="line">              </span><br><span class="line">                <span class="comment">// item 不为 null，说明 p 就是链表的头结点了</span></span><br><span class="line">                <span class="keyword">if</span> (item != <span class="literal">null</span> &amp;&amp; p.casItem(item, <span class="literal">null</span>)) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (p != h) <span class="comment">// 和跟新tail一样，不要每次出队一个节点，就更新一下 head（宁可多循环几次去找到真正的头结点）</span></span><br><span class="line">                        updateHead(h, ((q = p.next) != <span class="literal">null</span>) ? q : p);</span><br><span class="line">                    <span class="keyword">return</span> item;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> ((q = p.next) == <span class="literal">null</span>) &#123; <span class="comment">// p的item为null，p 又没有next节点，说明队列此时已经为empty了，返回null即可</span></span><br><span class="line">                    updateHead(h, p); <span class="comment">// 更新 head </span></span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (p == q)</span><br><span class="line">                    <span class="comment">// 和上面的一样，如果有其他 thread 也在 poll 节点的话，那么 p 引用的节点可能已经出队，标记为哨兵（哨兵的next指针指向自己）</span></span><br><span class="line">                    <span class="comment">// 这个时候需要从新的 head 出发寻找</span></span><br><span class="line">                    <span class="keyword">continue</span> restartFromHead;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    p = q; <span class="comment">// 往后移动，继续找</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 和上面的逻辑基本一致</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        restartFromHead:</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="keyword">for</span> (Node&lt;E&gt; h = head, p = h, q;;) &#123;</span><br><span class="line">                <span class="type">E</span> <span class="variable">item</span> <span class="operator">=</span> p.item;</span><br><span class="line">                <span class="keyword">if</span> (item != <span class="literal">null</span> || (q = p.next) == <span class="literal">null</span>) &#123;</span><br><span class="line">                    updateHead(h, p); <span class="comment">// updateHead 自身也做了一次 h 是否等于 p 的判断的</span></span><br><span class="line">                    <span class="keyword">return</span> item;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (p == q)</span><br><span class="line">                    <span class="keyword">continue</span> restartFromHead;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    p = q;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 返回列表中的第一个活动（未删除）节点，如果没有，则返回 null。</span></span><br><span class="line">    <span class="comment">// 这是 poll/peek 的另一种变体； 这里返回第一个节点，而不是元素。 </span></span><br><span class="line">    <span class="comment">// 作为类内的方法使用，不公开</span></span><br><span class="line">    Node&lt;E&gt; <span class="title function_">first</span><span class="params">()</span> &#123;</span><br><span class="line">        restartFromHead:</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="keyword">for</span> (Node&lt;E&gt; h = head, p = h, q;;) &#123;</span><br><span class="line">                <span class="type">boolean</span> <span class="variable">hasItem</span> <span class="operator">=</span> (p.item != <span class="literal">null</span>);</span><br><span class="line">                <span class="keyword">if</span> (hasItem || (q = p.next) == <span class="literal">null</span>) &#123;</span><br><span class="line">                    updateHead(h, p);</span><br><span class="line">                    <span class="keyword">return</span> hasItem ? p : <span class="literal">null</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (p == q)</span><br><span class="line">                    <span class="keyword">continue</span> restartFromHead;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    p = q;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 不是通过 size == 0 来判断的</span></span><br><span class="line">    <span class="comment">// 时间复杂度: O(1)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> first() == <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 需要遍历整个链表，因为没有成员属性记录元素个数</span></span><br><span class="line">    <span class="comment">// 时间复杂度: O(n), 谨慎使用</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (Node&lt;E&gt; p = first(); p != <span class="literal">null</span>; p = succ(p)) <span class="comment">// 注意是 succ(p) 而不是简单的 p.next</span></span><br><span class="line">            <span class="keyword">if</span> (p.item != <span class="literal">null</span>)</span><br><span class="line">                <span class="keyword">if</span> (++count == Integer.MAX_VALUE)</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">return</span> count;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">remove</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (o != <span class="literal">null</span>) &#123;</span><br><span class="line">            Node&lt;E&gt; next, pred = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">for</span> (Node&lt;E&gt; p = first(); p != <span class="literal">null</span>; pred = p, p = next) &#123;</span><br><span class="line">                <span class="type">boolean</span> <span class="variable">removed</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">                <span class="type">E</span> <span class="variable">item</span> <span class="operator">=</span> p.item;</span><br><span class="line">                <span class="keyword">if</span> (item != <span class="literal">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (!o.equals(item)) &#123;</span><br><span class="line">                        next = succ(p);</span><br><span class="line">                        <span class="keyword">continue</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    removed = p.casItem(item, <span class="literal">null</span>);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                next = succ(p);</span><br><span class="line">                <span class="keyword">if</span> (pred != <span class="literal">null</span> &amp;&amp; next != <span class="literal">null</span>) <span class="comment">// unlink</span></span><br><span class="line">                    pred.casNext(p, next); <span class="comment">// cas 失败也 ok</span></span><br><span class="line">                <span class="keyword">if</span> (removed)</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">checkNotNull</span><span class="params">(Object v)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (v == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * UNSAFE 的 cas </span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">casTail</span><span class="params">(Node&lt;E&gt; cmp, Node&lt;E&gt; val)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> UNSAFE.compareAndSwapObject(<span class="built_in">this</span>, tailOffset, cmp, val);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">casHead</span><span class="params">(Node&lt;E&gt; cmp, Node&lt;E&gt; val)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> UNSAFE.compareAndSwapObject(<span class="built_in">this</span>, headOffset, cmp, val);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Unsafe mechanics</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> sun.misc.Unsafe UNSAFE;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> headOffset;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> tailOffset;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            UNSAFE = sun.misc.Unsafe.getUnsafe();</span><br><span class="line">            Class&lt;?&gt; k = ConcurrentLinkedQueue.class;</span><br><span class="line">            headOffset = UNSAFE.objectFieldOffset</span><br><span class="line">                (k.getDeclaredField(<span class="string">&quot;head&quot;</span>));</span><br><span class="line">            tailOffset = UNSAFE.objectFieldOffset</span><br><span class="line">                (k.getDeclaredField(<span class="string">&quot;tail&quot;</span>));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/15/BlockingQueue/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/15/BlockingQueue/" class="post-title-link" itemprop="url">JDK的BlockingQueue</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-15 00:07:32" itemprop="dateCreated datePublished" datetime="2021-12-15T00:07:32+08:00">2021-12-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-03-15 17:42:53" itemprop="dateModified" datetime="2022-03-15T17:42:53+08:00">2022-03-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JUC/" itemprop="url" rel="index"><span itemprop="name">JUC</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="BlockingQueue"><a href="#BlockingQueue" class="headerlink" title="BlockingQueue"></a>BlockingQueue</h2><p><code>BlockingQueue</code>接口，作为阻塞队列的接口规范，有多种实现类。</p>
<p><code>BlockingQueue</code>有四种形式，处理不能立即满足但可能在未来某个时候满足的操作。</p>
<p>四种方式各不相同：一个抛出异常，第二个返回一个特殊值（ <code>null</code>或<code>false</code> ，取决于操作），第三个无限期地阻塞当前线程，直到操作成功，第四个阻塞仅给定的最大时间限制，然后放弃。 这些方法总结在下表中：</p>
<table>
<thead>
<tr>
<th>方法&#x2F;特点</th>
<th>Throws exception</th>
<th>Special value</th>
<th>Blocks</th>
<th>Time out</th>
</tr>
</thead>
<tbody><tr>
<td>Insert</td>
<td>add(e)</td>
<td>offer(e)</td>
<td>put(e)</td>
<td>offer(e, time, unit)</td>
</tr>
<tr>
<td>Remove</td>
<td>remove()</td>
<td>poll()</td>
<td>take()</td>
<td>poll(time, unit)</td>
</tr>
<tr>
<td>Examine</td>
<td>element</td>
<td>peek</td>
<td>not applicable</td>
<td>not applicable</td>
</tr>
</tbody></table>
<h2 id="LinkedBlockingQueue"><a href="#LinkedBlockingQueue" class="headerlink" title="LinkedBlockingQueue"></a>LinkedBlockingQueue</h2><blockquote>
<p><strong>基于链表的(有界的)阻塞队列</strong>。</p>
<p>该队列对元素 FIFO（先进先出）进行排序。 </p>
<p>队列的头部是在队列中停留时间最长的那个元素。 队列的尾部是在队列中停留时间最短的那个元素。 新元素插入队列尾部，队列检索操作获取队列头部元素。 </p>
<p><strong>链表实现的阻塞度列通常比基于数组的阻塞队列具有更高的吞吐量</strong>, 但是没有提供公平访问策略，在大多数并发应用程序中性能的可预测较差。<br>可选的指定容量构造函数参数是一种防止队列过度扩展的方法。</p>
<p>如果未指定，容量等于Integer.MAX_VALUE 。链接节点在每次插入时动态创建，除非这会使队列超出容量。<br><strong>不允许null元素</strong>。</p>
</blockquote>
<p>实现原理</p>
<p><strong>两把锁，两个条件变量</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** Lock held by take, poll, etc */</span></span><br><span class="line">    <span class="comment">// 节点出队操作所需要获取的锁</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">takeLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Wait queue for waiting takes */</span></span><br><span class="line">    <span class="comment">// 队列不空的条件通知（不空，说明可以出队）</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Condition</span> <span class="variable">notEmpty</span> <span class="operator">=</span> takeLock.newCondition();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Lock held by put, offer, etc */</span></span><br><span class="line">    <span class="comment">// 节点入队操作所需要获取的锁</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">putLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Wait queue for waiting puts */</span></span><br><span class="line">    <span class="comment">// 队列不满的条件通知（不满，说明可以入队）</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Condition</span> <span class="variable">notFull</span> <span class="operator">=</span> putLock.newCondition();</span><br></pre></td></tr></table></figure>





<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> java.util.concurrent;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LinkedBlockingQueue</span>&lt;E&gt; <span class="keyword">extends</span> <span class="title class_">AbstractQueue</span>&lt;E&gt;</span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">BlockingQueue</span>&lt;E&gt;, java.io.Serializable &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> -<span class="number">6903933977591709194L</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;E&gt; &#123;</span><br><span class="line">        E item;</span><br><span class="line">        Node&lt;E&gt; next;</span><br><span class="line"></span><br><span class="line">        Node(E x) &#123; item = x; &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 队列限制，如果没有指定为 Integer.MAX_VALUE</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> capacity;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 当前元素数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">count</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(); <span class="comment">// 如果不是的，自增、自减都是线程不安全的</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 哨兵头</span></span><br><span class="line"><span class="comment">     * 不变量: head.item == null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">transient</span> Node&lt;E&gt; head;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 哨兵尾</span></span><br><span class="line"><span class="comment">     * 不变量: last.item == null</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">transient</span> Node&lt;E&gt; last;</span><br><span class="line">  </span><br><span class="line">  	<span class="comment">// 也就是说empty的queue也至少有1个哨兵（初始时，head == last ）</span></span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Lock held by take, poll, etc */</span></span><br><span class="line">    <span class="comment">// 节点出队操作所需要获取的锁</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">takeLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Wait queue for waiting takes */</span></span><br><span class="line">    <span class="comment">// 队列不空的条件通知（不空，说明可以出队）</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Condition</span> <span class="variable">notEmpty</span> <span class="operator">=</span> takeLock.newCondition();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Lock held by put, offer, etc */</span></span><br><span class="line">    <span class="comment">// 节点入队操作所需要获取的锁</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">putLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Wait queue for waiting puts */</span></span><br><span class="line">    <span class="comment">// 队列不满的条件通知（不满，说明可以入队）</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Condition</span> <span class="variable">notFull</span> <span class="operator">=</span> putLock.newCondition();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 通知那些因为队列已空而等待的节点出队线程</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">signalNotEmpty</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">takeLock</span> <span class="operator">=</span> <span class="built_in">this</span>.takeLock;</span><br><span class="line">        takeLock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            notEmpty.signal(); <span class="comment">// 注意不是 signalAll(), 只通知一个线程, 下面也是一样</span></span><br><span class="line">         &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            takeLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">     <span class="comment">// 通知那些因为队列已满而等待的节点入队线程</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">signalNotFull</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">putLock</span> <span class="operator">=</span> <span class="built_in">this</span>.putLock;</span><br><span class="line">        putLock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            notFull.signal(); </span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            putLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 入队</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">enqueue</span><span class="params">(Node&lt;E&gt; node)</span> &#123;</span><br><span class="line">        <span class="comment">// assert putLock.isHeldByCurrentThread(); 断言当前线程拿到 putLock</span></span><br><span class="line">        <span class="comment">// assert last.next == null; 断言last确实是zu&#x27;h</span></span><br><span class="line">        last = last.next = node;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// </span></span><br><span class="line">    <span class="keyword">private</span> E <span class="title function_">dequeue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// assert takeLock.isHeldByCurrentThread(); 断言当前线程拿到 takeLock</span></span><br><span class="line">        Node&lt;E&gt; h = head;</span><br><span class="line">        Node&lt;E&gt; first = h.next;</span><br><span class="line">        h.next = h; <span class="comment">// help GC</span></span><br><span class="line">        head = first;</span><br><span class="line">        <span class="type">E</span> <span class="variable">x</span> <span class="operator">=</span> first.item;</span><br><span class="line">        first.item = <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 两把锁都加锁</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">fullyLock</span><span class="params">()</span> &#123;</span><br><span class="line">        putLock.lock();</span><br><span class="line">        takeLock.lock();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 两把锁都解锁</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">fullyUnlock</span><span class="params">()</span> &#123;</span><br><span class="line">        takeLock.unlock();</span><br><span class="line">        putLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LinkedBlockingQueue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>(Integer.MAX_VALUE);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">LinkedBlockingQueue</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (capacity &lt;= <span class="number">0</span>) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>();</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">        last = head = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;E&gt;(<span class="literal">null</span>); <span class="comment">// 一个就ok</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> count.get();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 剩余可用容量</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">remainingCapacity</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> capacity - count.get();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 如果空间不够用吗，一直阻塞</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(E e)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">if</span> (e == <span class="literal">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();</span><br><span class="line">        <span class="comment">// Note: convention in all put/take/etc is to preset local var</span></span><br><span class="line">        <span class="comment">// holding count negative to indicate failure unless set.</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        Node&lt;E&gt; node = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;E&gt;(e);</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">putLock</span> <span class="operator">=</span> <span class="built_in">this</span>.putLock;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">count</span> <span class="operator">=</span> <span class="built_in">this</span>.count;</span><br><span class="line">        putLock.lockInterruptibly(); <span class="comment">// 可中断的加锁</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (count.get() == capacity) &#123; <span class="comment">// 注意是 while</span></span><br><span class="line">                notFull.await(); <span class="comment">// 等待过程中，也可能被 interrupt</span></span><br><span class="line">            &#125;</span><br><span class="line">            enqueue(node);</span><br><span class="line">            c = count.getAndIncrement(); <span class="comment">// 先get后incr，也就是说先拿到队列此时的容量 c</span></span><br><span class="line">            <span class="keyword">if</span> (c + <span class="number">1</span> &lt; capacity) <span class="comment">// c + 1 &lt; capacity 队列一定不满，因为此时 putLock 被自己占有，如果有其他 take 线程，队列已用空间只会更小</span></span><br><span class="line">                notFull.signal();  <span class="comment">// 通知其他put线程工作（注意只通知一个）</span></span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * 为什么，put 线程还需要通知其他 put 线程呢？</span></span><br><span class="line"><span class="comment">             * 从信号量的角度讲，只需要 take 线程去通知即可。</span></span><br><span class="line"><span class="comment">             * 这就是作者所说的级联通知，而且每次都通知一个，避免大量线程同时被唤醒去争抢锁，</span></span><br><span class="line"><span class="comment">             * 并且避免了不必要的（加锁--通知--解锁）方法调用。</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            putLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (c == <span class="number">0</span>) <span class="comment">// 如果已有其他 take 线程在等待，现在通知它们工作</span></span><br><span class="line">            signalNotEmpty();</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 为什么是 == 而不是 &gt;= ?</span></span><br><span class="line"><span class="comment">         * 实际上，直接去掉这个 if 判断，直接调用 signalNotEmpty() 也是正确的。</span></span><br><span class="line"><span class="comment">         * 但是这会导致每次put调用后都用调用signalNotEmpty（这一步会加锁在解锁），增大太大开销。</span></span><br><span class="line"><span class="comment">         * 所以，相当于只在队列为 empty 时入队元素，put 线程才会通知 take 线程，并且也只通知一个。 </span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 指定等待时间，其余代码与上面一致，只是在等待固定时间后会 return false </span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E e, <span class="type">long</span> timeout, TimeUnit unit)</span></span><br><span class="line">        <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (e == <span class="literal">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();</span><br><span class="line">        <span class="type">long</span> <span class="variable">nanos</span> <span class="operator">=</span> unit.toNanos(timeout);</span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">putLock</span> <span class="operator">=</span> <span class="built_in">this</span>.putLock;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">count</span> <span class="operator">=</span> <span class="built_in">this</span>.count;</span><br><span class="line">        putLock.lockInterruptibly();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (count.get() == capacity) &#123;</span><br><span class="line">                <span class="keyword">if</span> (nanos &lt;= <span class="number">0</span>)</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">                nanos = notFull.awaitNanos(nanos); <span class="comment">// awaitNanos(nanos) 会返回需要等待的剩余时间，&lt;= 0 表明等待结束了  </span></span><br><span class="line">            &#125;</span><br><span class="line">            enqueue(<span class="keyword">new</span> <span class="title class_">Node</span>&lt;E&gt;(e));</span><br><span class="line">            c = count.getAndIncrement();</span><br><span class="line">            <span class="keyword">if</span> (c + <span class="number">1</span> &lt; capacity)</span><br><span class="line">                notFull.signal();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            putLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (c == <span class="number">0</span>)</span><br><span class="line">            signalNotEmpty();</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 不阻塞</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">offer</span><span class="params">(E e)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (e == <span class="literal">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();</span><br><span class="line">        <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">count</span> <span class="operator">=</span> <span class="built_in">this</span>.count;</span><br><span class="line">        <span class="comment">// 如果容量已满，直接返回即可</span></span><br><span class="line">        <span class="keyword">if</span> (count.get() == capacity)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">      </span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        Node&lt;E&gt; node = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;E&gt;(e);</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">putLock</span> <span class="operator">=</span> <span class="built_in">this</span>.putLock;</span><br><span class="line">        putLock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (count.get() &lt; capacity) &#123;</span><br><span class="line">                enqueue(node);</span><br><span class="line">                c = count.getAndIncrement();</span><br><span class="line">                <span class="keyword">if</span> (c + <span class="number">1</span> &lt; capacity)</span><br><span class="line">                    notFull.signal();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            putLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (c == <span class="number">0</span>)</span><br><span class="line">            signalNotEmpty();</span><br><span class="line">        <span class="keyword">return</span> c &gt;= <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">take</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        E x;</span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">count</span> <span class="operator">=</span> <span class="built_in">this</span>.count;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">takeLock</span> <span class="operator">=</span> <span class="built_in">this</span>.takeLock;</span><br><span class="line">        takeLock.lockInterruptibly();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (count.get() == <span class="number">0</span>) &#123;</span><br><span class="line">                notEmpty.await();</span><br><span class="line">            &#125;</span><br><span class="line">            x = dequeue();</span><br><span class="line">            c = count.getAndDecrement();</span><br><span class="line">            <span class="keyword">if</span> (c &gt; <span class="number">1</span>) <span class="comment">// 级联通知</span></span><br><span class="line">                notEmpty.signal();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            takeLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (c == capacity)</span><br><span class="line">            signalNotFull();</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 与上面的逻辑类似，只在队列仅剩余一个可用位置的时候，才去通知 put 线程，并且只通知一个</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// 下面的逻辑类似，不再赘述</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">(<span class="type">long</span> timeout, TimeUnit unit)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">E</span> <span class="variable">x</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="type">long</span> <span class="variable">nanos</span> <span class="operator">=</span> unit.toNanos(timeout);</span><br><span class="line">        <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">count</span> <span class="operator">=</span> <span class="built_in">this</span>.count;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">takeLock</span> <span class="operator">=</span> <span class="built_in">this</span>.takeLock;</span><br><span class="line">        takeLock.lockInterruptibly();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (count.get() == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (nanos &lt;= <span class="number">0</span>)</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">                nanos = notEmpty.awaitNanos(nanos);</span><br><span class="line">            &#125;</span><br><span class="line">            x = dequeue();</span><br><span class="line">            c = count.getAndDecrement();</span><br><span class="line">            <span class="keyword">if</span> (c &gt; <span class="number">1</span>)</span><br><span class="line">                notEmpty.signal();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            takeLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (c == capacity)</span><br><span class="line">            signalNotFull();</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">poll</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">count</span> <span class="operator">=</span> <span class="built_in">this</span>.count;</span><br><span class="line">        <span class="keyword">if</span> (count.get() == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">E</span> <span class="variable">x</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">takeLock</span> <span class="operator">=</span> <span class="built_in">this</span>.takeLock;</span><br><span class="line">        takeLock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (count.get() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                x = dequeue();</span><br><span class="line">                c = count.getAndDecrement();</span><br><span class="line">                <span class="keyword">if</span> (c &gt; <span class="number">1</span>)</span><br><span class="line">                    notEmpty.signal();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            takeLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (c == capacity)</span><br><span class="line">            signalNotFull();</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 加 take 锁检测</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">peek</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (count.get() == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">takeLock</span> <span class="operator">=</span> <span class="built_in">this</span>.takeLock;</span><br><span class="line">        takeLock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Node&lt;E&gt; first = head.next;</span><br><span class="line">            <span class="keyword">if</span> (first == <span class="literal">null</span>)</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                <span class="keyword">return</span> first.item;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            takeLock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// contains调用时，直接不允许 put、take</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">contains</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (o == <span class="literal">null</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        fullyLock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (Node&lt;E&gt; p = head.next; p != <span class="literal">null</span>; p = p.next)</span><br><span class="line">                <span class="keyword">if</span> (o.equals(p.item))</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            fullyUnlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>迭代器:</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 返回弱一致性的迭代器</span></span><br><span class="line"><span class="keyword">public</span> Iterator&lt;E&gt; <span class="title function_">iterator</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Itr</span>();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">class</span> <span class="title class_">Itr</span> <span class="keyword">implements</span> <span class="title class_">Iterator</span>&lt;E&gt; &#123;</span><br><span class="line">   </span><br><span class="line">    <span class="keyword">private</span> Node&lt;E&gt; current;</span><br><span class="line">    <span class="keyword">private</span> Node&lt;E&gt; lastRet;</span><br><span class="line">    <span class="comment">// 缓存住，即使后来这个节点出队了，只要 hasNext() 返回 true, 也一定可以正常返回，</span></span><br><span class="line">    <span class="comment">// 也正因此，它是弱一致性的。</span></span><br><span class="line">    <span class="keyword">private</span> E currentElement;</span><br><span class="line"></span><br><span class="line">    Itr() &#123;</span><br><span class="line">        fullyLock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            current = head.next;</span><br><span class="line">            <span class="keyword">if</span> (current != <span class="literal">null</span>)</span><br><span class="line">                currentElement = current.item;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            fullyUnlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasNext</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> current != <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">private</span> Node&lt;E&gt; <span class="title function_">nextNode</span><span class="params">(Node&lt;E&gt; p)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            Node&lt;E&gt; s = p.next;</span><br><span class="line">            <span class="keyword">if</span> (s == p)  <span class="comment">// 标志着这个节点已经被出队了。</span></span><br><span class="line">                <span class="keyword">return</span> head.next;</span><br><span class="line">            <span class="keyword">if</span> (s == <span class="literal">null</span> || s.item != <span class="literal">null</span>)</span><br><span class="line">                <span class="keyword">return</span> s;</span><br><span class="line">            p = s;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * next、remove之前，都会争抢两把锁，同时锁住。</span></span><br><span class="line"><span class="comment">     * 也就是说，使用迭代器遍历链表的时候</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">next</span><span class="params">()</span> &#123;</span><br><span class="line">        fullyLock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (current == <span class="literal">null</span>)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NoSuchElementException</span>();</span><br><span class="line">            <span class="type">E</span> <span class="variable">x</span> <span class="operator">=</span> currentElement;</span><br><span class="line">            lastRet = current;</span><br><span class="line">            current = nextNode(current);</span><br><span class="line">            currentElement = (current == <span class="literal">null</span>) ? <span class="literal">null</span> : current.item;</span><br><span class="line">            <span class="keyword">return</span> x;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            fullyUnlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (lastRet == <span class="literal">null</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>();</span><br><span class="line">        fullyLock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Node&lt;E&gt; node = lastRet;</span><br><span class="line">            lastRet = <span class="literal">null</span>;</span><br><span class="line">            <span class="keyword">for</span> (Node&lt;E&gt; trail = head, p = trail.next;</span><br><span class="line">                 p != <span class="literal">null</span>;</span><br><span class="line">                 trail = p, p = p.next) &#123;</span><br><span class="line">                <span class="keyword">if</span> (p == node) &#123;</span><br><span class="line">                    unlink(p, trail);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            fullyUnlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="ArrayBlockingQueue"><a href="#ArrayBlockingQueue" class="headerlink" title="ArrayBlockingQueue"></a>ArrayBlockingQueue</h2><blockquote>
<p><strong>由数组支持的有界阻塞队列</strong>。</p>
<p>这是一个经典的“有界缓冲区”，其中一个固定大小的数组保存由生产者插入并由消费者提取的元素。<br>容量一旦创建，就无法更改。 </p>
<p>尝试put元素放入已满队列将导致操作阻塞； 尝试从空队列中take元素也会类似地阻塞。</p>
<p><strong>此类支持用于排序等待生产者和消费者线程的可选公平策略</strong>。 </p>
<p>默认情况下，不保证此顺序。 但是，在公平性设置为true时构造的队列以 FIFO 顺序授予线程访问权限。</p>
<p><strong>公平通常会降低吞吐量，但会减少可变性并避免饥饿</strong>。</p>
</blockquote>
<p>实现原理</p>
<p><strong>一把锁，两个条件变量</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 这里只使用了一把锁，也就是说，put/take 线程会互相竞争。</span></span><br><span class="line"><span class="comment"> * 吞吐量自然也没有 LinkedBlockingQueue 大，但是提供了公平性策略。</span></span><br><span class="line"><span class="comment"> */</span> </span><br><span class="line"><span class="keyword">final</span> ReentrantLock lock;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Condition notEmpty;</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Condition notFull;</span><br></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> java.util.concurrent;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ArrayBlockingQueue</span>&lt;E&gt; <span class="keyword">extends</span> <span class="title class_">AbstractQueue</span>&lt;E&gt;</span><br><span class="line">        <span class="keyword">implements</span> <span class="title class_">BlockingQueue</span>&lt;E&gt;, java.io.Serializable &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> -<span class="number">817911632652898426L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** The queued items */</span></span><br><span class="line">    <span class="keyword">final</span> Object[] items;</span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 在此实现中，数组允许被填充满，而不是最多为 length - 1</span></span><br><span class="line"><span class="comment">     */</span> </span><br><span class="line">  </span><br><span class="line">    <span class="comment">/** items index for next take, poll, peek or remove */</span></span><br><span class="line">    <span class="comment">// 下一个要取元素的位置，实际上也就是队列的第一个元素</span></span><br><span class="line">    <span class="type">int</span> takeIndex;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** items index for next put, offer, or add */</span></span><br><span class="line">    <span class="comment">// 下一个要放置元素的位置，实际上也就是队列的最后一个元素的下一个位置。</span></span><br><span class="line">    <span class="type">int</span> putIndex;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/** Number of elements in the queue */</span></span><br><span class="line">    <span class="comment">// 元素个数</span></span><br><span class="line">    <span class="type">int</span> count; <span class="comment">// 注意不是 atomic的</span></span><br><span class="line">	</span><br><span class="line">  	</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 这里只使用了一把锁，也就是说，put/take 线程会互相竞争。</span></span><br><span class="line"><span class="comment">     * 吞吐量自然也没有 LinkedBlockingQueue 大，但是提供了公平性策略。</span></span><br><span class="line"><span class="comment">     */</span> </span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Condition notEmpty;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Condition notFull;</span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 循环减 1</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">dec</span><span class="params">(<span class="type">int</span> i)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> ((i == <span class="number">0</span>) ? items.length : i) - <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">enqueue</span><span class="params">(E x)</span> &#123;</span><br><span class="line">        <span class="comment">// assert lock.getHoldCount() == 1;</span></span><br><span class="line">        <span class="comment">// assert items[putIndex] == null;</span></span><br><span class="line">        <span class="keyword">final</span> Object[] items = <span class="built_in">this</span>.items;</span><br><span class="line">        items[putIndex] = x;</span><br><span class="line">        <span class="keyword">if</span> (++putIndex == items.length)</span><br><span class="line">            putIndex = <span class="number">0</span>;</span><br><span class="line">        count++;</span><br><span class="line">        notEmpty.signal(); <span class="comment">// 入队，通知非空</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> E <span class="title function_">dequeue</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// assert lock.getHoldCount() == 1;</span></span><br><span class="line">        <span class="comment">// assert items[takeIndex] != null;</span></span><br><span class="line">        <span class="keyword">final</span> Object[] items = <span class="built_in">this</span>.items;</span><br><span class="line">        <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span></span><br><span class="line">        <span class="type">E</span> <span class="variable">x</span> <span class="operator">=</span> (E) items[takeIndex];</span><br><span class="line">        items[takeIndex] = <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">if</span> (++takeIndex == items.length)</span><br><span class="line">            takeIndex = <span class="number">0</span>;</span><br><span class="line">        count--;</span><br><span class="line">        <span class="keyword">if</span> (itrs != <span class="literal">null</span>)</span><br><span class="line">            itrs.elementDequeued();</span><br><span class="line">        notFull.signal(); <span class="comment">// 出队，通知非满</span></span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 由于底层是固定大小的数组，所以必须指定初始容量，默认是非公平的</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ArrayBlockingQueue</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>(capacity, <span class="literal">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ArrayBlockingQueue</span><span class="params">(<span class="type">int</span> capacity, <span class="type">boolean</span> fair)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (capacity &lt;= <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>();</span><br><span class="line">        <span class="built_in">this</span>.items = <span class="keyword">new</span> <span class="title class_">Object</span>[capacity];</span><br><span class="line">        lock = <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>(fair);</span><br><span class="line">        notEmpty = lock.newCondition();</span><br><span class="line">        notFull =  lock.newCondition();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">   </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(E e)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        checkNotNull(e);</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="built_in">this</span>.lock;</span><br><span class="line">        lock.lockInterruptibly();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (count == items.length)</span><br><span class="line">                notFull.await();</span><br><span class="line">            enqueue(e);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">    <span class="keyword">public</span> E <span class="title function_">take</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="built_in">this</span>.lock;</span><br><span class="line">        lock.lockInterruptibly();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (count == <span class="number">0</span>)</span><br><span class="line">                notEmpty.await();</span><br><span class="line">            <span class="keyword">return</span> dequeue();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// size 方法也会加锁</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="built_in">this</span>.lock;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> count;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/12/JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/12/JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84/" class="post-title-link" itemprop="url">JVM内存结构</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-12 18:31:40" itemprop="dateCreated datePublished" datetime="2021-12-12T18:31:40+08:00">2021-12-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-12 23:05:29" itemprop="dateModified" datetime="2022-09-12T23:05:29+08:00">2022-09-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JVM/" itemprop="url" rel="index"><span itemprop="name">JVM</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="JVM内存结构图"><a href="#JVM内存结构图" class="headerlink" title="JVM内存结构图"></a>JVM内存结构图</h2><p>《深入理解Java虚拟机（第2版）》</p>
<p><img src="/2021/12/12/JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84/jvm-memory.jpg"></p>
<p><strong>Java8内存结构介绍</strong></p>
<p><img src="/2021/12/12/JVM%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84/jvm-memory2.png" alt="图片"></p>
<h2 id="虚拟机内存与本地内存的区别"><a href="#虚拟机内存与本地内存的区别" class="headerlink" title="虚拟机内存与本地内存的区别"></a>虚拟机内存与本地内存的区别</h2><p>Java虚拟机在执行的时候会把管理的内存分配成不同的区域，这些区域被称为虚拟机内存，同时，对于虚拟机没有直接管理的物理内存，也有一定的利用，这些被利用却不在虚拟机内存数据区的内存，我们称它为<code>本地内存</code>，这两种内存有一定的区别：</p>
<ul>
<li><p>JVM内存<br>受虚拟机内存大小的参数控制，当大小超过参数设置的大小时就会报OOM</p>
</li>
<li><p>本地内存<br>本地内存不受虚拟机内存参数的限制，只受物理内存容量的限制<br>虽然不受参数的限制，但是如果内存的占用超出物理内存的大小，同样也会报OOM</p>
</li>
</ul>
<h2 id="各个区域的具体作用"><a href="#各个区域的具体作用" class="headerlink" title="各个区域的具体作用"></a>各个区域的具体作用</h2><h3 id="程序计数器（Program-Counter-Register）"><a href="#程序计数器（Program-Counter-Register）" class="headerlink" title="程序计数器（Program Counter Register）"></a>程序计数器（Program Counter Register）</h3><p><strong>程序计数器就是当前线程所执行的字节码的行号指示器</strong>，<strong>通过改变计数器的值，来选取下一行指令</strong>，通过他来实现跳转、循环、恢复线程等功能。</p>
<ul>
<li>在任何时刻，一个处理器内核只能运行一个线程，多线程是通过线程轮流切换，分配时间来完成的，这就需要有一个标志来记住每个线程执行到了哪里，这里便需要到了程序计数器。</li>
<li>所以，<strong>程序计数器是线程私有的</strong>，每个线程都已自己的程序计数器。</li>
</ul>
<h3 id="虚拟机栈（JVM-Stacks）"><a href="#虚拟机栈（JVM-Stacks）" class="headerlink" title="虚拟机栈（JVM Stacks）"></a>虚拟机栈（JVM Stacks）</h3><p>虚拟机栈是线程私有的，随线程生灭。虚拟机栈描述的是线程中的方法的内存模型：</p>
<p>每个方法被执行的时候，都会在虚拟机栈中同步创建一个<strong>栈帧</strong>（stack frame）。</p>
<ul>
<li>每个栈帧的包含如下的内容<ul>
<li>Java基本数据类型（byte&#x2F;boolean&#x2F;char&#x2F;int&#x2F;long&#x2F;double&#x2F;float&#x2F;short）以及对象的引用 （注：这里的基本数据类型指的是方法内的局部变量）</li>
<li>局部变量表</li>
<li>操作数栈</li>
<li>动态连接</li>
<li>方法返回地址</li>
</ul>
</li>
</ul>
<p>方法被执行时入栈，执行完后出栈<br>虚拟机栈可能会抛出两种异常：</p>
<ul>
<li>如果线程请求的栈深度大于虚拟机所规定的栈深度，则会抛出<code>StackOverFlowError</code>即栈溢出</li>
<li>如果虚拟机的栈容量可以动态扩展，那么当虚拟机栈申请不到内存时会抛出<code>OutOfMemoryError</code>即OOM内存溢出</li>
</ul>
<h3 id="本地方法栈（Native-Method-Stacks）"><a href="#本地方法栈（Native-Method-Stacks）" class="headerlink" title="本地方法栈（Native Method Stacks）"></a>本地方法栈（Native Method Stacks）</h3><p>本地方法栈与虚拟机栈的作用是相似的,都会抛出<code>OutOfMemoryError</code>和<code>StackOverFlowError</code>，都是<code>线程私有</code>的，主要的区别在于：</p>
<ul>
<li>虚拟机栈执行的是java方法</li>
<li>本地方法栈执行的是native方法</li>
</ul>
<h3 id="Java堆（Java-Heap）"><a href="#Java堆（Java-Heap）" class="headerlink" title="Java堆（Java Heap）"></a>Java堆（Java Heap）</h3><p>java堆是JVM内存中最大的一块，由所有<strong>线程共享</strong>,是<strong>由垃圾收集器管理</strong>的内存区域，<strong>主要存放对象实例</strong>，当然由于java虚拟机的发展，堆中也多了许多东西，现在主要有：</p>
<ul>
<li><p>对象实例</p>
<ul>
<li>类初始化生成的对象；</li>
<li>基本数据类型的数组也是对象实例。</li>
</ul>
</li>
<li><p>字符串常量池</p>
<ul>
<li>字符串常量池原本存放于方法区，jdk7开始放置于堆中；</li>
<li>字符串常量池存储的是string对象的直接引用，而不是直接存放的对象，是一张<code>string table</code>。</li>
</ul>
</li>
<li><p>静态变量<br>静态变量是有static修饰的变量，jdk7时从方法区迁移至堆中。</p>
</li>
<li><p>线程分配缓冲区（Thread Local Allocation Buffer）</p>
<ul>
<li>线程私有，但是不影响java堆的共性；</li>
<li>增加线程分配缓冲区是为了提升对象分配时的效率。</li>
</ul>
</li>
</ul>
<p>java堆既可以是固定大小的，也可以是可扩展的（通过参数<code>-Xmx</code>和<code>-Xms</code>设定），如果堆无法扩展或者无法分配内存时也会报OOM。</p>
<blockquote>
<p>静态变量 存储位置：JDK8之前，静态成员变量确实存放在方法区；但JDK8之后就取消了“永久代”，取而代之的是“元空间”，永久代中的数据也进行了迁移，静态成员变量迁移到了堆中（方法区是JVM的规范，永久代是方法区的具体实现）。</p>
</blockquote>
<h3 id="方法区（Method-Area）"><a href="#方法区（Method-Area）" class="headerlink" title="方法区（Method Area）"></a>方法区（Method Area）</h3><p>方法区绝对是网上所有关于java内存结构文章争论的焦点，因为方法区的实现在java8做了一次大革新，现在我们来讨论一下：</p>
<p><strong>方法区是所有线程共享的内存</strong>，</p>
<p>在java8以前是放在JVM内存中的，<strong>由永久代实现，受JVM内存大小参数的限制</strong>;</p>
<p>在java8中移除了永久代的内容，<strong>方法区由元空间(Meta Space)实现，并直接放到了本地内存中</strong>，不受JVM参数的限制（当然，如果物理内存被占满了，方法区也会报OOM）。</p>
<p>并且将原来放在方法区的<strong>字符串常量池</strong>和<strong>静态变量</strong>都转移到了Java堆中，方法区与其他区域不同的地方在于，方法区在编译期间和类加载完成后的内容有少许不同，不过总的来说分为这两部分：</p>
<ul>
<li><p>类元信息（Klass）</p>
<ul>
<li>类元信息在类编译期间放入方法区，里面放置了类的基本信息，包括类的<code>版本</code>、<code>字段</code>、<code>方法</code>、<code>接口</code>以及<code>常量池表（Constant Pool Table）</code></li>
<li><code>常量池表（Constant Pool Table）</code>存储了类在编译期间生成的<code>字面量</code>、<code>符号引用</code>这些信息在类加载完后会被解析到运行时常量池中</li>
</ul>
</li>
<li><p>运行时常量池（Runtime Constant Pool）</p>
<ul>
<li><strong>运行时常量池主要存放在类加载后被解析的字面量与符号引用</strong>，但不止这些；</li>
<li>运行时常量池具备动态性，可以添加数据，比较多的使用就是String类的intern()方法</li>
</ul>
</li>
</ul>
<h2 id="直接内存"><a href="#直接内存" class="headerlink" title="直接内存"></a>直接内存</h2><p><strong>直接内存位于本地内存，不属于JVM内存</strong>，但是也会在物理内存耗尽的时候报OOM。</p>
<p>在jdk1.4中加入了NIO（New Input&#x2F;Putput）类，引入了一种基于通道（channel）与缓冲区（buffer）的新IO方式，它可以使用native函数直接分配堆外内存，然后通过存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作，这样可以在一些场景下大大提高IO性能，<strong>避免了在java堆和native堆来回复制数据</strong>。</p>
<h1 id="常见问题"><a href="#常见问题" class="headerlink" title="常见问题"></a>常见问题</h1><h2 id="类常量池、运行时常量池、字符串常量池有什么关系？有什么区别？"><a href="#类常量池、运行时常量池、字符串常量池有什么关系？有什么区别？" class="headerlink" title="类常量池、运行时常量池、字符串常量池有什么关系？有什么区别？"></a>类常量池、运行时常量池、字符串常量池有什么关系？有什么区别？</h2><p>类常量池与运行时常量池都存储在方法区，而<strong>字符串常量池在jdk7时就已经从方法区迁移到了java堆中</strong>。</p>
<p>在类编译过程中，会把类元信息放到方法区，类元信息的其中一部分便是<strong>类常量池，主要存放字面量和符号引用</strong>，<strong>而字面量的一部分便是文本字符</strong>，在类加载时将字面量和符号引用解析为直接引用存储在运行时常量池；</p>
<p>对于文本字符来说，它们会在解析时查找字符串常量池，查出这个文本字符对应的字符串对象的直接引用，将直接引用存储在运行时常量池；</p>
<p><strong>字符串常量池存储的是字符串对象的引用，而不是字符串本身</strong>。</p>
<h2 id="什么是字面量？什么是符号引用？"><a href="#什么是字面量？什么是符号引用？" class="headerlink" title="什么是字面量？什么是符号引用？"></a>什么是字面量？什么是符号引用？</h2><ul>
<li><p>字面量</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> a=<span class="number">1</span>;<span class="comment">//这个1便是字面量</span></span><br><span class="line">String b=<span class="string">&quot;iloveu&quot;</span>;<span class="comment">//iloveu便是字面量</span></span><br></pre></td></tr></table></figure>
</li>
<li><ul>
<li>java代码在编译过程中是无法构建引用的，字面量就是在编译时对于数据的一种表示:</li>
</ul>
</li>
<li><p>符号引用</p>
</li>
<li><ul>
<li>由于在编译过程中并不知道每个类的地址，因为可能这个类还没有加载，所以如果你在一个类中引用了另一个类，那么你完全无法知道他的内存地址，那怎么办，我们只能用他的类名作为符号引用，在类加载完后用这个符号引用去获取他的内存地址。</li>
<li>例子：我在com.demo.Solution类中引用了com.test.Quest，那么我会把<code>com.test.Quest</code>作为符号引用存到类常量池，等类加载完后，拿着这个引用去方法区找这个类的内存地址。</li>
</ul>
</li>
</ul>
<blockquote>
<p>参考</p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/ityouknow/p/5610232.html">jvm系列(二):JVM内存结构</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/" class="post-title-link" itemprop="url">JVM垃圾回收算法和垃圾回收器</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-12 18:31:40" itemprop="dateCreated datePublished" datetime="2021-12-12T18:31:40+08:00">2021-12-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-05 03:04:06" itemprop="dateModified" datetime="2023-01-05T03:04:06+08:00">2023-01-05</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JVM/" itemprop="url" rel="index"><span itemprop="name">JVM</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="GC的基础知识"><a href="#GC的基础知识" class="headerlink" title="GC的基础知识"></a>GC的基础知识</h1><h2 id="一、垃圾回收的区域"><a href="#一、垃圾回收的区域" class="headerlink" title="一、垃圾回收的区域"></a>一、垃圾回收的区域</h2><ul>
<li>栈：栈中的生命周期是跟随线程，所以一般不需要关注。</li>
<li>堆：堆中的对象是垃圾回收的重点。</li>
<li>方法区：这一块也会发生垃圾回收，不过这块的效率比较低，一般不是我们关注的重点。</li>
</ul>
<h2 id="二、怎么判断对象的存活"><a href="#二、怎么判断对象的存活" class="headerlink" title="二、怎么判断对象的存活"></a>二、怎么判断对象的存活</h2><p>一般有两种方式（引用计数法、可达性分析），<strong>JVM使用的是可达性分析</strong>。</p>
<h3 id="1-引用计数法"><a href="#1-引用计数法" class="headerlink" title="1. 引用计数法"></a>1. 引用计数法</h3><p>给对象添加一个引用计数器，当对象增加一个引用时计数器加 1，引用失效时计数器减 1。引用计数为 0 的对象可被回收（Python 在用，但主流虚拟机没有使用）。</p>
<ul>
<li>优点：快，方便，实现简单。</li>
<li>缺陷：对象相互引用时（A.instance&#x3D;B 同时 B.instance&#x3D;A），很难判断对象是否该回收。</li>
</ul>
<h3 id="2-可达性分析"><a href="#2-可达性分析" class="headerlink" title="2. 可达性分析"></a>2. 可达性分析</h3><p>来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点，从这些节点开始向下搜索，搜索所走过的路径称为引用链（Reference Chain），当一个对象到 GC Roots 没有任何引用链相连时，则证明此对象是不可用的。</p>
<p>作为 GC Roots 的对象包括下面几种：</p>
<ul>
<li>虚拟机栈（栈帧中的本地变量表）中引用的对象；</li>
<li>方法区中类静态属性引用的对象；</li>
<li>方法区中常量引用的对象；</li>
<li>本地方法栈中JNI（即一般说的Native方法）引用的对象；</li>
</ul>
<h2 id="三、-垃圾回收算法"><a href="#三、-垃圾回收算法" class="headerlink" title="三、 垃圾回收算法"></a>三、 垃圾回收算法</h2><p>本节具体介绍一下各种垃圾回收算法的思想：</p>
<h3 id="1-标记-清除算法"><a href="#1-标记-清除算法" class="headerlink" title="1. 标记-清除算法"></a>1. 标记-清除算法</h3><p>标记-清除算法对<strong>根集合</strong>进行扫描，对<strong>存活</strong>的对象进行标记。标记完成后，再对整个空间内<strong>未被标记</strong>的对象扫描，进行回收。</p>
<ul>
<li><p><strong>优点</strong>：</p>
<p><strong>实现简单，不需要进行对象进行移动</strong>。</p>
</li>
<li><p><strong>缺点</strong>：</p>
<p>标记、清除过程效率低，<strong>产生大量不连续的内存碎片</strong>，提高了垃圾回收的频率。</p>
</li>
</ul>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/mark-sweep.png"></p>
<h3 id="2-标记-复制算法"><a href="#2-标记-复制算法" class="headerlink" title="2. 标记-复制算法"></a>2. 标记-复制算法</h3><p>这种收集算法解决了标记清除算法存在的效率问题。它将内存区域划分成相同的两个<strong>内存块</strong>。每次仅使用一半的空间，<code>JVM</code>生成的新对象放在一半空间中。当一半空间用完时进行<code>GC</code>，把可到达对象复制到另一半空间，然后把使用过的内存空间一次清理掉。</p>
<ul>
<li><p><strong>优点</strong>：</p>
<p>按顺序分配内存即可，实现简单、运行高效，不用考虑内存碎片。</p>
</li>
<li><p><strong>缺点</strong>：</p>
<p><strong>可用的内存大小缩小为原来的一半</strong>，<strong>对象存活率高时会频繁进行复制</strong>。</p>
</li>
</ul>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-copy.png"></p>
<h3 id="3-标记-整理算法"><a href="#3-标记-整理算法" class="headerlink" title="3. 标记-整理算法"></a>3. 标记-整理算法</h3><p><strong>标记-整理算法</strong> 采用和 <strong>标记-清除算法</strong> 一样的方式进行对象的标记，但后续不直接对可回收对象进行清理，而是将所有的<strong>存活对象</strong>往一端<strong>空闲空间</strong>移动，然后清理掉端边界以外的内存空间。</p>
<ul>
<li><p><strong>优点</strong>：</p>
<p><strong>解决了标记-清理算法存在的内存碎片问题</strong>。</p>
</li>
<li><p><strong>缺点</strong>：</p>
<p>仍<strong>需要进行局部对象移动，一定程度上降低了效率</strong>。</p>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/mark-compact.png"></p>
</li>
</ul>
<h3 id="4-分代收集技术"><a href="#4-分代收集技术" class="headerlink" title="4. 分代收集技术"></a>4. 分代收集技术</h3><h4 id="基本术语"><a href="#基本术语" class="headerlink" title="基本术语"></a>基本术语</h4><ul>
<li>部分收集（Partial GC）指目标不是完整收集整个Java堆的垃圾收集，其中可以分为：</li>
<li><ul>
<li>新生代收集（Minor GC&#x2F;Young GC）: 指目标只是新生代的垃圾收集；</li>
<li>老年代收集（Major GC&#x2F;Old GC）: 指目标只是老年代的垃圾收集。注意这个名词可能有歧义，有的也指整堆收集。</li>
<li>混合收集（Mixed GC）：指目标是收集整个新生代以及部分老年代的来及收集。目前只有G1收集器有这种行为。</li>
</ul>
</li>
<li>整堆收集（Full GC）：收集整个Java堆和方法区的垃圾收集。</li>
</ul>
<p>当前商业虚拟机都采用<strong>分代收集</strong>的垃圾收集算法。分代收集算法，顾名思义是根据对象的<strong>存活周期</strong>将内存划分为几块。一般包括<strong>年轻代</strong>、<strong>老年代</strong> 和 <strong>永久代</strong>，如图所示：</p>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-generation.png"></p>
<blockquote>
<p>上图中的 永久（Permanent）代在 JDK8之后便被取消。</p>
</blockquote>
<h4 id="新生代（Young-generation）"><a href="#新生代（Young-generation）" class="headerlink" title="新生代（Young generation）"></a>新生代（Young generation）</h4><p>绝大多数最新被创建的对象会被分配到这里，由于<strong>大部分对象</strong>在创建后会很快变得<strong>不可达</strong>，所以很多对象被创建在<strong>新生代</strong>，然后<strong>消失</strong>。对象从这个区域消失的过程我们称之为 <code>minor GC</code>。</p>
<p><strong>新生代</strong> 中存在一个<code>Eden</code>区和两个<code>Survivor</code>区（Survivor 区里面分为 from 和 to 区），每次只使用Eden和其中的一块Survivor区。新对象会首先分配在<code>Eden</code>中（<strong>如果新对象过大，会直接分配在老年代</strong>中）。在<code>GC</code>中，<code>Eden</code>中的对象会被移动到<code>Survivor</code>中，直至对象满足一定的年纪（定义为熬过<code>GC</code>的次数），会被移动到<strong>老年代</strong>。</p>
<p>可以设置<strong>新生代</strong>和<strong>老年代</strong>的相对大小。这种方式的优点是新生代大小会随着整个<strong>堆</strong>大小<strong>动态扩展</strong>。参数 <code>-XX:NewRatio</code> 设置<strong>老年代</strong>与<strong>新生代</strong>的比例。例如 <code>-XX:NewRatio=8</code> 指定 <strong>老年代&#x2F;新生代</strong> 为<code>8/1</code>. <strong>老年代</strong> 占堆大小的 <code>7/8</code> ，<strong>新生代</strong> 占堆大小的 <code>1/8</code>（默认即是 <code>1/8</code>）。</p>
<p><strong>当Eden区满了的时候，会触发Young GC。</strong></p>
<h4 id="老年代（Old-generation）"><a href="#老年代（Old-generation）" class="headerlink" title="老年代（Old generation）"></a>老年代（Old generation）</h4><p>对象没有变得不可达，并且从新生代中<strong>存活</strong>下来，会被<strong>拷贝</strong>到这里。其所占用的空间要比新生代多。也正由于其相对<strong>较大的空间</strong>，发生在<strong>老年代</strong>上的<code>GC</code>要比<strong>新生代</strong>要<strong>少得多</strong>。对象从<strong>老年代</strong>中消失的过程，可以称之为<code>major GC</code>（或者<code>full GC</code>）。</p>
<p><code>JDK8</code>堆内存一般是划分为<strong>年轻代</strong>和<strong>老年代</strong>，<strong>不同年代</strong> 根据自身特性采用<strong>不同的垃圾收集算法</strong>。</p>
<p>对于<strong>新生代</strong>，每次<code>GC</code>时都有<strong>大量</strong>的对象死亡，只有<strong>少量</strong>对象存活。考虑到复制成本低，适合采用<strong>复制算法</strong>。因此有了<code>From Survivor</code>和<code>To Survivor</code>区域。</p>
<p>对于<strong>老年代</strong>，因为对象<strong>存活率高</strong>，没有额外的内存空间对它进行担保。因而适合采用<strong>标记-清除算法</strong>和<strong>标记-整理算法</strong>进行回收。</p>
<h1 id="垃圾回收器"><a href="#垃圾回收器" class="headerlink" title="垃圾回收器"></a>垃圾回收器</h1><p>Jvm 垃圾回收器把上面的三种算法全部用到了，采用分代收集。</p>
<h2 id="新生代：复制算法。"><a href="#新生代：复制算法。" class="headerlink" title="新生代：复制算法。"></a>新生代：复制算法。</h2><table>
<thead>
<tr>
<th>收集器</th>
<th>收集对象和算法</th>
<th>收集器类型</th>
</tr>
</thead>
<tbody><tr>
<td>Serial</td>
<td>新生代，<strong>标记复制算法</strong></td>
<td>单线程</td>
</tr>
<tr>
<td>ParNew</td>
<td>新生代，<strong>标记复制算法</strong></td>
<td>并行的多线程收集器</td>
</tr>
<tr>
<td>Parallel Scavenge</td>
<td>新生代，<strong>标记复制算法</strong></td>
<td>并行的多线程收集器</td>
</tr>
</tbody></table>
<h2 id="老年代：标记清除算法和标记整理算法"><a href="#老年代：标记清除算法和标记整理算法" class="headerlink" title="老年代：标记清除算法和标记整理算法"></a>老年代：标记清除算法和标记整理算法</h2><table>
<thead>
<tr>
<th>收集器</th>
<th>收集对象和算法</th>
<th>收集器类型</th>
</tr>
</thead>
<tbody><tr>
<td>Serial Old</td>
<td>老年代，标记整理算法</td>
<td>单线程</td>
</tr>
<tr>
<td>Parallel Old</td>
<td>老年代，标记整理算法</td>
<td>并行的多线程收集器</td>
</tr>
<tr>
<td>CMS（Concurrent Mark Sweep ）</td>
<td>老年代，<strong>标记清除算法</strong></td>
<td>并行和并发收集器</td>
</tr>
<tr>
<td>G1（Garbage First）</td>
<td>跨新生代和老年代，复制算法 + 标记整理算法</td>
<td>并行和并发收集器</td>
</tr>
</tbody></table>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-machines.png"></p>
<p><strong>注：</strong></p>
<ul>
<li><strong>并行：垃圾收集的多线程的同时进行</strong>。</li>
<li><strong>并发：垃圾收集的多线程和用户应用的多线程同时进行</strong>。</li>
<li>使用 <code>jps -v</code> 可以看到使用的垃圾收集器，例如：<code>-XX:+UseConcMarkSweepGC</code> （CMS）</li>
</ul>
<h2 id="Serial-x2F-Serial-Old"><a href="#Serial-x2F-Serial-Old" class="headerlink" title="Serial&#x2F;Serial Old"></a>Serial&#x2F;Serial Old</h2><p>最古老的，单线程，独占式，成熟，适合单 CPU 服务器。<code>-XX:+UseSerialGC</code> 新生代和老年代都用串行收集器。</p>
<h2 id="ParNew"><a href="#ParNew" class="headerlink" title="ParNew"></a>ParNew</h2><p>ParNew 和 Serial 基本没区别，唯一的区别：多线程，多 CPU 的，停顿时间比 Serial 少 。</p>
<p><code>-XX:+UseParNewGC</code>新生代使用 ParNew，老年代使用 Serial Old 。</p>
<p>可以和CMS搭配使用。</p>
<h2 id="Parallel-Scavenge（ParallerGC）-x2F-Parallel-Old"><a href="#Parallel-Scavenge（ParallerGC）-x2F-Parallel-Old" class="headerlink" title="Parallel Scavenge（ParallerGC）&#x2F;Parallel Old"></a>Parallel Scavenge（ParallerGC）&#x2F;Parallel Old</h2><p><strong>关注吞吐量的垃圾收集器</strong>，<strong>高吞吐量则可以高效率地利用 CPU 时间</strong>（但对响应时间有负面影响），尽快完成程序的运算任务，主要适合在后台运算而不需要太多交互的任务。所谓吞吐量就是 CPU 用于运行用户代码的时间与 CPU 总消耗时间的比值，即吞吐量&#x3D;运行用户代码时间&#x2F;（运行用户代码时间+垃圾收集时间）</p>
<h2 id="CMS（Concurrent-Mark-Sweep）"><a href="#CMS（Concurrent-Mark-Sweep）" class="headerlink" title="CMS（Concurrent Mark Sweep）"></a>CMS（Concurrent Mark Sweep）</h2><p>CMS收集器是一种<strong>以获取最短回收停顿时间为目标</strong>的收集器。目前很大一部分的 Java 应用集中在互联网站或者 B&#x2F;S 系统的服务端上，这类应用尤其<strong>重视服务的响应速度，希望系统停顿时间最短</strong>，以给用户带来较好的体验。</p>
<p>CMS 收集器就非常符合这类应用的需求。<code>-XX:+UseConcMarkSweepGC</code> ，<strong>一般新生代使用 ParNew，老年代的用 CMS</strong>，从名字（包含“Mark Sweep”）上就可以看出，CMS 收集器是基于“标记—清除”算法实现的，它的运作过程相对于前面几种收集器来说更复杂一些。</p>
<h3 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h3><p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-cms.png"></p>
<p>整个过程分为 4 个步骤，包括：</p>
<p>1、<strong>初始标记</strong>：仅仅只是标记一下 <strong>GC Roots 能直接关联到的对象</strong>，速度很快，需要停顿（STW -Stop the world）。（需要 STW）</p>
<p>2、<strong>并发标记</strong>：从 GC Root 开始对堆中对象进行可达性分析，找到存活对象，它在整个回收过程中耗时最长，不需要停顿。（无需STW）</p>
<p>3、<strong>重新标记</strong>：<strong>为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录，</strong>。这个阶段的停顿时间一般会比初始标记阶段稍长一些，但远比并发标记的时间短。(因为用户在并发标记阶段对对象做的修改相对来讲是比较少的)。（需要STW)</p>
<ol start="4">
<li><strong>并发清除</strong>：（无需STW）</li>
</ol>
<blockquote>
<p><strong>Stop The World</strong></p>
<p>Java中Stop-The-World机制简称STW，是在执行垃圾收集算法时，Java应用程序的其他所有线程都被挂起（除了垃圾收集帮助器之外）。Java中一种全局暂停现象，全局停顿，所有Java代码停止，native代码可以执行，但不能与JVM交互。</p>
</blockquote>
<h3 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h3><ol>
<li>优点</li>
</ol>
<p>由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作，所以，从<strong>总体上来说，CMS 收集器的内存回收过程是与用户线程一起并发执行的</strong>。</p>
<ol start="2">
<li>缺点</li>
</ol>
<ul>
<li><strong>CPU 资源敏感</strong>：因为并发阶段多线程占据 CPU 资源，如果 CPU 资源不足，效率会明显降低。</li>
<li><strong>会产生内存碎片</strong>：<strong>标记-清除算法</strong> 会导致产生不连续的内存碎片。</li>
<li>由于 CMS <strong>并发清理阶段</strong> 用户线程还在运行着，伴随程序运行自然就还会有新的垃圾不断产生，这一部分垃圾出现在标记过程之后，CMS 无法在当次收集中处理掉它们，只好留待下一次 GC 时再清理掉。这一部分垃圾就称为 <strong>浮动垃圾</strong>。</li>
<li>由于浮动垃圾的存在，因此需要预留出一部分内存，意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。在 1.6 的版本中老年代空间使用率阈值(92%)，如果预留的内存不够存放浮动垃圾，就会出现 Concurrent Mode Failure，这时虚拟机将临时启用 Serial Old 来替代 CMS。</li>
</ul>
<h2 id="G1"><a href="#G1" class="headerlink" title="G1"></a>G1</h2><p><strong>G1相比较CMS的改进</strong></p>
<ul>
<li><strong>基于标记-整理算法, 不会产生空间碎片</strong>，分配大对象时不会无法得到连续的空间而提前触发一次full gc 。</li>
<li><strong>停顿时间相对可控</strong>： G1可以通过设置预期停顿时间（Pause time）来控制垃圾收集时间，但是<strong>这个预期停顿时间G1只能尽量做到，而不是一定能做到</strong>。</li>
</ul>
<p>G1 收集器之所以能建立可预测的停顿时间模型，是因为它可以<strong>有计划地避免在整个 Java 堆中进行全区域的垃圾收集</strong>。</p>
<p>G1 跟踪各个 Region 里面的垃圾堆积的价值大小（回收所获得的空间大小以及回收所需时间的经验值），<strong>在后台维护一个优先列表</strong>，<strong>每次根据允许的收集时间，优先回收价值最大的 Region</strong>（这也就是 <strong>Garbage-First</strong> 名称的来由）。</p>
<p><strong>这种使用 Region 划分内存空间以及有优先级的区域回收方式</strong>，<strong>保证了 G1 收集器在有限的时间内可以获取尽可高的收集效率</strong>。</p>
<h3 id="Region如何划分"><a href="#Region如何划分" class="headerlink" title="Region如何划分"></a>Region如何划分</h3><p>G1 把堆划分成多个大小相等的 <strong>独立区域</strong>（Region），<strong>新生代和老年代不再物理隔离</strong>。</p>
<p>G1 算法将堆划分为若干个独立区域（Region），它仍然属于分代收集器。</p>
<p>不过，这些区域的一部分包含新生代，<strong>新生代的垃圾收集依然采用暂停所有应用线程的方式，将存活对象拷贝到老年代或者 Survivor 空间</strong>。</p>
<p>例如其中一个独立区域如图：</p>
<p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/g1-region.png"></p>
<h3 id="步骤-1"><a href="#步骤-1" class="headerlink" title="步骤"></a>步骤</h3><p><img src="/2021/12/12/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E7%AE%97%E6%B3%95%E5%92%8C%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/gc-g1.png"></p>
<p>大致分为4个步骤：</p>
<ol>
<li><strong>初始标记</strong>：仅仅只是标记一下 GC Roots 能直接关联到的对象，并且修改 TAMS（Nest Top Mark Start）的值，让下一阶段用户程序并发运行时，能在正确可以的 Region 中创建对象，此阶段需要停顿线程(STW)，但耗时很短。</li>
<li><strong>并发标记</strong>：从 GC Root 开始对堆中对象进行可达性分析，找到存活对象，此阶段耗时较长，但可与用户程序并发执行。</li>
<li><strong>最终标记</strong>：<strong>为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录</strong>，虚拟机将这段时间对象变化记录在线程的 <code>Remembered Set Logs</code> 里面，最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中。这阶段需要停顿线程(STW)，但是可并行执行。</li>
<li><strong>筛选回收</strong>：<strong>首先对各个 Region 中的回收价值和成本进行排序，根据用户所期望的 GC 停顿时间来制定回收计划</strong>。此阶段其实也可以做到与用户程序一起并发执行，但是因为只回收一部分 Region，时间是用户可控制的，而且停顿用户线程将大幅度提高收集效率。</li>
</ol>
<p>G1 从整体来看是基于<strong>标记整理</strong>算法实现的收集器，从局部（两个Region之间）上来看是基于复制算法实现的。</p>
<h2 id="垃圾回收器的一些重要参数"><a href="#垃圾回收器的一些重要参数" class="headerlink" title="垃圾回收器的一些重要参数"></a>垃圾回收器的一些重要参数</h2><p>使用<code>java -XX:+PrintCommandLineFlags -version</code>查看关于垃圾收集器的一些信息。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC</span><br><span class="line">java version &quot;1.8.0_281&quot;</span><br><span class="line">Java(TM) SE Runtime Environment (build 1.8.0_281-b09)</span><br><span class="line">Java HotSpot(TM) 64-Bit Server VM (build 25.281-b09, mixed mode)</span><br></pre></td></tr></table></figure>



<table>
<thead>
<tr>
<th>参数</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td>UseSerialGC</td>
<td>虚拟机运行在 Client 模式下的默认值，打开此开关后，使用 Serial+Serial Old 的收集器组合进行内存回收</td>
</tr>
<tr>
<td>UseParNewGC</td>
<td>打开此开关后，使用 ParNew + Serial Old 的收集器组合进行内存回收</td>
</tr>
<tr>
<td>UseConcMarkSweepGC</td>
<td>打开此开关后，使用 ParNew + CMS + Serial Old 的收集器组合进行内存回收。Serial Old 收集器将作为 CMS 收集器出现 Concurrent Mode Failure 失败后的后备收集器使用</td>
</tr>
<tr>
<td><strong>UseParallelGC</strong></td>
<td><strong>虚拟机运行在 Server 模式下的默认值</strong>，打开此开关后，使用 <strong>Parallel Scavenge + Serial Old(PS MarkSweep)</strong> 的收集器组合进行内存回收</td>
</tr>
<tr>
<td>UseParallelOldGC</td>
<td>打开此开关后，使用 Parallel Scavenge + Parallel Old 的收集器组合进行内存回收</td>
</tr>
<tr>
<td>SurvivorRatio</td>
<td>新生代中 Eden 区域与 Survivor 区域的容量比值，默认为 8，代表 Eden : Survivor &#x3D; 8 : 1</td>
</tr>
<tr>
<td>PretenureSizeThreshold</td>
<td>直接晋升到老年代的对象大小，设置这个参数后，大于这个参数的对象将直接在老年代分配</td>
</tr>
<tr>
<td>MaxTenuringThreshold</td>
<td>晋升到老年代的对象年龄，每个对象在坚持过一次 Minor GC 之后，年龄就增加 1，当超过这个参数值时就进入老年代</td>
</tr>
<tr>
<td>UseAdaptiveSizePolicy</td>
<td>动态调整 Java 堆中各个区域的大小以及进入老年代的年龄</td>
</tr>
<tr>
<td>HandlePromotionFailure</td>
<td>是否允许分配担保失败，即老年代的剩余空间不足以应付新生代的整个 Eden 和 Survivor 区的所有对象都存活的极端情况</td>
</tr>
<tr>
<td>ParallelGCThreads</td>
<td>设置并行 GC 时进行内存回收的线程数</td>
</tr>
<tr>
<td>GCTimeRatio GC</td>
<td>时间占总时间的比率，默认值为 99，即允许 1% 的 GC 时间，仅在使用 Parallel Scavenge 收集器生效</td>
</tr>
<tr>
<td>MaxGCPauseMillis</td>
<td>设置 GC 的最大停顿时间，仅在使用 Parallel Scavenge 收集器时生效</td>
</tr>
<tr>
<td>CMSInitiatingOccupancyFraction</td>
<td>设置 CMS 收集器在老年代空间被使用多少后触发垃圾收集，默认值为 68%，仅在使用 CMS 收集器时生效</td>
</tr>
<tr>
<td>UseCMSCompactAtFullCollection</td>
<td>设置 CMS 收集器在完成垃圾收集后是否要进行一次内存碎片整理，仅在使用 CMS 收集器时生效</td>
</tr>
<tr>
<td>CMSFullGCsBeforeCompaction</td>
<td>设置 CMS 收集器在进行若干次垃圾收集后再启动一次内存碎片整理，仅在使用 CMS 收集器时生效</td>
</tr>
</tbody></table>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844904148404535309">JVM垃圾回收算法和垃圾回收器</a></p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844903639794843656">JVM系列(五) - JVM垃圾回收算法</a></p>
<p><a target="_blank" rel="noopener" href="https://cloud.tencent.com/developer/article/1582661">Full GC 和 Minor GC，傻傻分不清楚</a></p>
</blockquote>
<h1 id="FullGC"><a href="#FullGC" class="headerlink" title="FullGC"></a>FullGC</h1><h2 id="什么时候会触发FullGC"><a href="#什么时候会触发FullGC" class="headerlink" title="什么时候会触发FullGC"></a>什么时候会触发FullGC</h2><h3 id="System-gc-方法的调用"><a href="#System-gc-方法的调用" class="headerlink" title="System.gc()方法的调用"></a>System.gc()方法的调用</h3><p>此方法的调用是建议JVM进行Full GC,虽然只是建议而非一定,但很多情况下它会触发 Full GC,从而增加Full GC的频率,也即增加了间歇性停顿的次数。强烈影响系建议能不使用此方法就别使用，让虚拟机自己去管理它的内存，可通过通过<code>-XX:+ DisableExplicitGC</code>来禁止RMI调用System.gc。</p>
<h3 id="Metaspace区内存达到阈值"><a href="#Metaspace区内存达到阈值" class="headerlink" title="Metaspace区内存达到阈值"></a>Metaspace区内存达到阈值</h3><p>从JDK8开始，永久代(PermGen)的概念被废弃掉了，取而代之的是一个称为Metaspace的存储空间。Metaspace使用的是本地内存，而不是堆内存，也就是说在默认情况下Metaspace的大小只与本地内存大小有关。-XX:MetaspaceSize&#x3D;21810376B（约为20.8MB）超过这个值就会引发Full GC，这个值不是固定的，是会随着JVM的运行进行动态调整的，与此相关的参数还有多个，详细情况请参考<a target="_blank" rel="noopener" href="https://blog.csdn.net/bolg_hero/article/details/78189621">这篇文章</a></p>
<h3 id="老年代空间不足"><a href="#老年代空间不足" class="headerlink" title="老年代空间不足"></a>老年代空间不足</h3><blockquote>
<p><strong>Survivor区域对象晋升到老年代有两种情况</strong>：</p>
<p>一种是给每个对象定义一个对象计数器，如果对象在Eden区域出生，并且经过了第一次GC，那么就将他的年龄设置为1，在Survivor区域的对象每熬过一次GC，年龄计数器加一，等到到达默认值15时，就会被移动到老年代中，默认值可以通过-XX:MaxTenuringThreshold来设置。<br>另外一种情况是如果JVM发现Survivor区域中的相同年龄的对象占到所有对象的一半以上时，就会将大于这个年龄的对象移动到老年代，在这批对象在统计后发现可以晋升到老年代，但是发现老年代没有足够的空间来放置这些对象，这就会引起Full GC。</p>
</blockquote>
<p>老年代空间只有在<strong>新生代对象转入</strong>及<strong>创建大对象、大数组</strong>时才会出现不足的现象，</p>
<p>当执行Full GC后空间仍然不足，则抛出如下错误：<code>java.lang.OutOfMemoryError: Java heap space</code><br>为避免以上两种状况引起的Full GC，调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。</p>
<h3 id="堆中分配很大的对象"><a href="#堆中分配很大的对象" class="headerlink" title="堆中分配很大的对象"></a>堆中分配很大的对象</h3><p>这个参数可以通过<code>-XX:PretenureSizeThreshold</code>进行设定，大对象或者长期存活的对象进入老年代，典型的大对象就是很长的字符串或者数组，它们在被创建后会直接进入老年代，虽然可能新生代中的Eden区域可以放置这个对象，在要放置的时候JVM如果发现老年代的空间不足时，会触发GC。</p>
<p>所谓大对象，是指需要大量连续内存空间的java对象，例如很长的数组，<strong>此种对象会直接进入老年代，而老年代虽然有很大的剩余空间，但是无法找到足够大的连续空间来分配给当前对象</strong>，此种情况就会触发JVM进行Full GC。</p>
<p>为了解决这个问题，CMS垃圾收集器提供了一个可配置的参数，即-XX:+UseCMSCompactAtFullCollection开关参数，用于在“享受”完Full GC服务之后额外免费赠送一个碎片整理的过程，<strong>内存整理的过程无法并发的，空间碎片问题没有了，但提顿时间不得不变长了</strong>，JVM设计者们还提供了另外一个参数 -XX:CMSFullGCsBeforeCompaction,这个参数用于设置在执行多少次不压缩的Full GC后,跟着来一次带压缩的。</p>
<h3 id="统计得到的Minor-GC晋升到旧生代的平均大小大于老年代的剩余空间"><a href="#统计得到的Minor-GC晋升到旧生代的平均大小大于老年代的剩余空间" class="headerlink" title="统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间"></a>统计得到的Minor GC晋升到旧生代的平均大小大于老年代的剩余空间</h3><p>这是一个较为复杂的触发情况，Hotspot<strong>为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象</strong>，在进行Minor GC时，做了一个判断，<strong>如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间，那么就直接触发Full GC</strong>。</p>
<blockquote>
<p>例如程序第一次触发Minor GC后，有6MB的对象晋升到旧生代，那么当下一次Minor GC发生时，首先检查旧生代的剩余空间是否大于6MB，如果小于6MB，则执行Full GC（相当于，提前进行fullgc，而不是把新生代的对象移动过去之后发现内存不足才fullgc）。</p>
<p>当新生代采用PS GC时，方式稍有不同，PS GC是在Minor GC后也会检查，例如上面的例子中第一次Minor GC后，PS GC会检查此时旧生代的剩余空间是否大于6MB，如小于，则触发对旧生代的回收。<br>除了以上4种状况外，对于使用RMI来进行RPC或管理的Sun JDK应用而言，默认情况下会一小时执行一次Full GC。可通过在启动时通过- java -Dsun.rmi.dgc.client.gcInterval&#x3D;3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。</p>
</blockquote>
<h3 id="CMS-GC时出现promotion-failed和concurrent-mode-failure"><a href="#CMS-GC时出现promotion-failed和concurrent-mode-failure" class="headerlink" title="CMS GC时出现promotion failed和concurrent mode failure"></a>CMS GC时出现promotion failed和concurrent mode failure</h3><p>对于采用CMS进行老年代GC的程序而言，尤其要注意GC日志中是否有<strong>promotion failed</strong>和<strong>concurrent mode failure</strong>两种状况，当这两种状况出现时可能会触发Full GC。</p>
<p>promotion failed是在进行Minor GC时，survivor space放不下、对象只能放入老年代，而此时老年代也放不下造成的；concurrent mode failure是在</p>
<p>执行CMS GC的过程中同时有对象要放入老年代，而此时老年代空间不足造成的（有时候“空间不足”是CMS GC时当前的浮动垃圾过多导致暂时性的空间不足触发Full GC）。<br>应对措施为：增大survivor space、老年代空间或调低触发并发GC的比率，但在JDK 5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况，可通过设置-XX: CMSMaxAbortablePrecleanTime&#x3D;5（单位为ms）来避免。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>可以发现其实堆内存的Full GC一般都是<strong>两个原因</strong>引起的，<strong>要么是老年代内存过小，要么是老年代连续内存过小。</strong>无非是这两点，而<strong>元数据区Metaspace引发的Full GC可能是阈值引起的</strong>。</p>
<h2 id="如何排查"><a href="#如何排查" class="headerlink" title="如何排查"></a>如何排查</h2><p><strong>检测JVM堆的情况</strong></p>
<p>方法1. 可以使用JDK的bin目录下的jvisualvm.exe工具来进行实时监测，这个是图形化界面，最为直观，这是一个强大的工具。<br>方法2. 采用jps找到进行id，然后使用jstat -gc pid来实时进行检测。<br>方法3. 运行程序前设置-XX:+PrintGCDetails，-XX:+PrintGCDateStamps参数打印GC的详细信息进行分析。</p>
<blockquote>
<p>参考博客</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/Hollake/article/details/90484027">https://blog.csdn.net/Hollake/article/details/90484027</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/12/ConcurrentHashMap/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/12/ConcurrentHashMap/" class="post-title-link" itemprop="url">JUC之并发安全的HashMap —— ConcurrentHashMap</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-12 16:15:24" itemprop="dateCreated datePublished" datetime="2021-12-12T16:15:24+08:00">2021-12-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-03-22 15:19:33" itemprop="dateModified" datetime="2022-03-22T15:19:33+08:00">2022-03-22</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/JUC/" itemprop="url" rel="index"><span itemprop="name">JUC</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>本篇文章将要介绍的 ConcurrentHashMap 是 HashMap 的并发版本，它是线程安全的，并且在高并发的情境下，性能优于 Hashtable 很多。</p>
<h2 id="历史版本的演变"><a href="#历史版本的演变" class="headerlink" title="历史版本的演变"></a>历史版本的演变</h2><blockquote>
<p><em>一句话总结</em></p>
<p>从JDK7版本的ReentrantLock+Segment+HashEntry，到JDK8版本中synchronized+CAS+HashEntry+红黑树。</p>
</blockquote>
<h3 id="JDK7的ConcurrentHashMap"><a href="#JDK7的ConcurrentHashMap" class="headerlink" title="JDK7的ConcurrentHashMap"></a>JDK7的ConcurrentHashMap</h3><p>JDK7 采用<strong>分段锁技术</strong>，<strong>整个 Hash 表被分成多个段（默认为16段），每个段中会对应一个 Segment 段锁</strong>，<strong>段与段之间可以并发访问，但是多线程想要操作同一个段是需要获取锁的</strong>。所有的 put，get，remove 等方法都是根据键的 hash 值对应到相应的段中，然后尝试获取锁进行访问。</p>
<p>相当于通过增加锁的数量来降低锁的粒度（能支持 N 个 Segment 这么多次数的并发）。</p>
<p><img src="/2021/12/12/ConcurrentHashMap/jdk7-segment.png"></p>
<p>下图为JDK7的数据结构。</p>
<p><img src="/2021/12/12/ConcurrentHashMap/jdk7-segment2.webp"></p>
<p>Segment 是 ConcurrentHashMap 的一个内部类，主要的组成如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Segment</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">ReentrantLock</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line">       <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> <span class="number">2249069246763182397L</span>;</span><br><span class="line">       </span><br><span class="line">       <span class="comment">// 和 HashMap 中的 HashEntry 作用一样，真正存放数据的桶</span></span><br><span class="line">       <span class="keyword">transient</span> <span class="keyword">volatile</span> HashEntry&lt;K,V&gt;[] table;</span><br><span class="line">       <span class="keyword">transient</span> <span class="type">int</span> count;</span><br><span class="line">       <span class="keyword">transient</span> <span class="type">int</span> modCount;</span><br><span class="line">       <span class="keyword">transient</span> <span class="type">int</span> threshold;</span><br><span class="line">       <span class="keyword">final</span> <span class="type">float</span> loadFactor;</span><br><span class="line">       </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>原理上来说：ConcurrentHashMap 采用了分段锁技术，其中 Segment 继承于 ReentrantLock。不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理，理论上 ConcurrentHashMap 支持 CurrencyLevel (Segment 数组数量)的线程并发。每当一个线程占用锁访问一个 Segment 时，不会影响到其他的 Segment。</p>
<p>下面也来看看核心的 <code>put</code>、<code>get</code> 方法。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> V <span class="title function_">put</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">    Segment&lt;K,V&gt; s;</span><br><span class="line">    <span class="keyword">if</span> (value == <span class="literal">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();</span><br><span class="line">    <span class="type">int</span> <span class="variable">hash</span> <span class="operator">=</span> hash(key);</span><br><span class="line">    <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> (hash &gt;&gt;&gt; segmentShift) &amp; segmentMask;</span><br><span class="line">    <span class="keyword">if</span> ((s = (Segment&lt;K,V&gt;)UNSAFE.getObject          <span class="comment">// nonvolatile; recheck</span></span><br><span class="line">         (segments, (j &lt;&lt; SSHIFT) + SBASE)) == <span class="literal">null</span>) <span class="comment">//  in ensureSegment</span></span><br><span class="line">        s = ensureSegment(j);</span><br><span class="line">    <span class="keyword">return</span> s.put(key, hash, value, <span class="literal">false</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>首先是通过 key 定位到 Segment，之后在对应的 Segment 中进行具体的 put。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> V <span class="title function_">put</span><span class="params">(K key, <span class="type">int</span> hash, V value, <span class="type">boolean</span> onlyIfAbsent)</span> &#123;</span><br><span class="line">    HashEntry&lt;K,V&gt; node = tryLock() ? <span class="literal">null</span> :</span><br><span class="line">        scanAndLockForPut(key, hash, value); <span class="comment">// 尝试获取锁，如果获取失败肯定就有其他线程存在竞争</span></span><br><span class="line">    V oldValue;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        HashEntry&lt;K,V&gt;[] tab = table;</span><br><span class="line">        <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> (tab.length - <span class="number">1</span>) &amp; hash;</span><br><span class="line">        HashEntry&lt;K,V&gt; first = entryAt(tab, index);</span><br><span class="line">        <span class="keyword">for</span> (HashEntry&lt;K,V&gt; e = first;;) &#123;</span><br><span class="line">            <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123;</span><br><span class="line">                K k;</span><br><span class="line">                <span class="keyword">if</span> ((k = e.key) == key ||</span><br><span class="line">                    (e.hash == hash &amp;&amp; key.equals(k))) &#123;</span><br><span class="line">                    oldValue = e.value;</span><br><span class="line">                    <span class="keyword">if</span> (!onlyIfAbsent) &#123;</span><br><span class="line">                        e.value = value;</span><br><span class="line">                        ++modCount;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                e = e.next;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (node != <span class="literal">null</span>)</span><br><span class="line">                    node.setNext(first);</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    node = <span class="keyword">new</span> <span class="title class_">HashEntry</span>&lt;K,V&gt;(hash, key, value, first);</span><br><span class="line">                <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> count + <span class="number">1</span>;</span><br><span class="line">                <span class="keyword">if</span> (c &gt; threshold &amp;&amp; tab.length &lt; MAXIMUM_CAPACITY)</span><br><span class="line">                    rehash(node);</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                    setEntryAt(tab, index, node);</span><br><span class="line">                ++modCount;</span><br><span class="line">                count = c;</span><br><span class="line">                oldValue = <span class="literal">null</span>;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> oldValue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>scanAndLockForPut</code>为第一步操作：获取锁，尝试自旋获取锁（try lock），如果重试的次数达到了 <code>MAX_SCAN_RETRIES</code> 则改为阻塞锁（lock）获取，保证能获取成功。</p>
<p>再结合图看看 put 的流程。</p>
<ol>
<li>将当前 Segment 中的 table 通过 key 的 hashcode 定位到 HashEntry。</li>
<li>遍历该 HashEntry，如果不为空则判断传入的 key 和当前遍历的 key 是否相等，相等则覆盖旧的 value。</li>
<li>不为空则需要新建一个 HashEntry 并加入到 Segment 中，同时会先判断是否需要扩容。</li>
<li>最后会解除在 1 中所获取当前 Segment 的锁。</li>
</ol>
<p><code>get</code>的逻辑比价简单：</p>
<p>只需要将 Key 通过 Hash 之后定位到具体的 Segment ，再通过一次 Hash 定位到具体的元素上。</p>
<p>由于 HashEntry 中的 value 属性是用 volatile 关键词修饰的，保证了内存可见性，所以每次获取时都是最新值。</p>
<p>ConcurrentHashMap 的 get 方法是非常高效的，<strong>因为整个过程都不需要加锁</strong>。</p>
<h3 id="JDK8的ConcurrentHashMap"><a href="#JDK8的ConcurrentHashMap" class="headerlink" title="JDK8的ConcurrentHashMap"></a>JDK8的ConcurrentHashMap</h3><p>J<strong>DK8 取消了基于 Segment 的分段锁思想</strong>，<strong>改用 CAS + synchronized 控制并发操作</strong>，在某些方面提升了性能。</p>
<p><strong>并且追随 1.8 版本的 HashMap 底层实现，使用数组+链表+红黑树进行数据存储</strong>。本篇主要介绍 1.8 版本的 ConcurrentHashMap 的具体实现。</p>
<h3 id="put流程"><a href="#put流程" class="headerlink" title="put流程"></a>put流程</h3><ul>
<li>根据 key 计算出 hashcode 。</li>
<li>判断是否需要进行初始化。</li>
<li><code>f</code> 即为当前 key 定位出的 Node，<strong>如果为空表示当前位置可以写入数据，利用 CAS 尝试写入，失败则自旋保证成功</strong>。</li>
<li>如果当前位置的 <code>hashcode == MOVED == -1</code>,则需要进行扩容。</li>
<li>如果都不满足，则利用 synchronized 锁写入数据。</li>
<li>如果数量大于 <code>TREEIFY_THRESHOLD</code> 则要转换为红黑树。</li>
</ul>
<p>有关其之前版本的实现情况，这里推荐几篇文章：</p>
<p><a target="_blank" rel="noopener" href="http://www.jianshu.com/p/e694f1e868ec">谈谈ConcurrentHashMap1.7和1.8的不同实现</a><br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/everSeeker/p/5601861.html">ConcurrentHashMap在jdk1.8中的改进</a><br><a target="_blank" rel="noopener" href="https://www.cnblogs.com/study-everyday/p/6430462.html">ConcurrentHashMap原理分析（1.7与1.8）</a></p>
<h3 id="JDK7与JDK8的区别"><a href="#JDK7与JDK8的区别" class="headerlink" title="JDK7与JDK8的区别"></a>JDK7与JDK8的区别</h3><ol>
<li><strong>底层数据结构</strong>：<strong>取消了Segment分段锁的数据结构</strong>，取而代之的是Node数组+链表+红黑树的结构，从而实现了对每一行数据进行加锁，进一步减少并发冲突的概率。</li>
</ol>
<blockquote>
<p>Node类成员变量Node的元素val和指针next都标注volatile，目的是在多线程环境下<strong>线程A修改结点的val或者新增节点的时候是对线程B可见的</strong>。</p>
</blockquote>
<blockquote>
<p>ConcurrentHashMap有成员变量transient volatile Node&lt;K,V&gt;[] table，此volatile的目的是<strong>为了使Node数组在扩容的时候对其他线程具有可见性</strong>。（例如：volatile int array[10]是指array的地址是volatile的而不是数组元素的值是volatile的.）</p>
</blockquote>
<ol start="2">
<li><strong>保证线程安全机制</strong>：JDK7采用segment的分段锁机制实现线程安全，其中segment继承自ReentrantLock。<strong>JDK8采用CAS(读)+Synchronized(写)保证线程安全</strong>。</li>
<li><strong>锁的粒度</strong>：原来是对需要进行数据操作的Segment加锁<strong>，JDK8调整为对每个数组元素加锁（Node）</strong>。</li>
<li><strong>链表转化为红黑树</strong>：定位结点的hash算法简化会带来弊端，Hash冲突加剧，因此在链表节点数量大于8时，会将链表转化为红黑树进行存储。</li>
<li><strong>查询时间复杂度</strong>：从原来的遍历链表O(n)，变成遍历红黑树O(logN)。</li>
</ol>
<h3 id="Collections-synchronizedMap、Hashtable、ConcurrentHashMap区别"><a href="#Collections-synchronizedMap、Hashtable、ConcurrentHashMap区别" class="headerlink" title="Collections.synchronizedMap、Hashtable、ConcurrentHashMap区别"></a>Collections.synchronizedMap、Hashtable、ConcurrentHashMap区别</h3><blockquote>
<p>参考文章</p>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/yangming1996/p/8031199.html">为并发而生的 ConcurrentHashMap（Java 8）</a></p>
<p><a target="_blank" rel="noopener" href="https://juejin.cn/post/6844903813892014087">解读Java8中ConcurrentHashMap是如何保证线程安全的</a></p>
<p><a target="_blank" rel="noopener" href="https://crossoverjie.top/2018/07/23/java-senior/ConcurrentHashMap/">HashMap? ConcurrentHashMap? 相信看完这篇没人能难住你！</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/10/Sandbox%E7%9A%84%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/10/Sandbox%E7%9A%84%E5%AE%89%E5%85%A8%E6%9C%BA%E5%88%B6/" class="post-title-link" itemprop="url">Sandbox的安全机制</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-10 21:51:54" itemprop="dateCreated datePublished" datetime="2021-12-10T21:51:54+08:00">2021-12-10</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-02 22:44:06" itemprop="dateModified" datetime="2022-09-02T22:44:06+08:00">2022-09-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/sandbox/" itemprop="url" rel="index"><span itemprop="name">sandbox</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="Sandbox的安全机制如何设计？"><a href="#Sandbox的安全机制如何设计？" class="headerlink" title="Sandbox的安全机制如何设计？"></a>Sandbox的安全机制如何设计？</h1><p>对于Linux而言，它的风格是一贯的。沙盒是进程而不是线程，这点非常明确。</p>
<h2 id="用户权限相关"><a href="#用户权限相关" class="headerlink" title="用户权限相关"></a>用户权限相关</h2><h3 id="Nobody-用户"><a href="#Nobody-用户" class="headerlink" title="Nobody 用户"></a>Nobody 用户</h3><p>在许多Unix系统与类Unix系统（如Linux）中，nobody是一个没有任何权限的用户。<br>该用户不拥有任何文件，也没有任何特殊权限。某些系统还会定义类似的用户组“nogroup”。<br>示例：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * @Author: 吉松阳</span></span><br><span class="line"><span class="comment"> * @Date: 2021/9/23</span></span><br><span class="line"><span class="comment"> * @Description: </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;pwd.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">passwd</span> *<span class="title">pw</span>;</span></span><br><span class="line">    <span class="type">char</span> *username = <span class="string">&quot;nobody&quot;</span>;</span><br><span class="line">    pw = getpwnam(username);</span><br><span class="line">    <span class="keyword">if</span> (!pw) &#123;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%s is not exist\n&quot;</span>, username);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;pw-&gt;pw_name = %s\n&quot;</span>, pw-&gt;pw_name);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;pw-&gt;pw_passwd = %s\n&quot;</span>, pw-&gt;pw_passwd);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;pw-&gt;pw_uid = %d\n&quot;</span>, pw-&gt;pw_uid);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;pw-&gt;pw_gid = %d\n&quot;</span>, pw-&gt;pw_gid);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;pw-&gt;pw_gecos = %s\n&quot;</span>, pw-&gt;pw_gecos);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;pw-&gt;pw_dir = %s\n&quot;</span>, pw-&gt;pw_dir);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;pw-&gt;pw_shell = %s\n&quot;</span>, pw-&gt;pw_shell);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>下面是 MacOS Big Sur 上的 nobody 用户相关信息。<br>其中<code>/var/empty</code>表明它不拥有任何文件，<code>/usr/bin/false</code>表明它不能登录使用shell</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">pw-&gt;pw_name = nobody</span><br><span class="line">pw-&gt;pw_passwd = *</span><br><span class="line">pw-&gt;pw_uid = -2</span><br><span class="line">pw-&gt;pw_gid = -2</span><br><span class="line">pw-&gt;pw_gecos = Unprivileged User</span><br><span class="line">pw-&gt;pw_dir = /var/empty</span><br><span class="line">pw-&gt;pw_shell = /usr/bin/false</span><br></pre></td></tr></table></figure>

<p>在运行oj用户的代码的时候，是以nobody的身份运行的，意味着它的权限非常有限，不能去执行那些危险的代码。</p>
<h3 id="root-用户"><a href="#root-用户" class="headerlink" title="root 用户"></a>root 用户</h3><p>root用户，即系统的管理员。<br>sandbox程序本身需要 root权限。<br>如何区分呢？<br>将上面的代码中的用户名替换成 root, 输出为：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_">pw-&gt;</span><span class="language-bash">pw_name = root</span></span><br><span class="line"><span class="meta prompt_">pw-&gt;</span><span class="language-bash">pw_passwd = *</span></span><br><span class="line"><span class="meta prompt_">pw-&gt;</span><span class="language-bash">pw_uid = 0</span></span><br><span class="line"><span class="meta prompt_">pw-&gt;</span><span class="language-bash">pw_gid = 0</span></span><br><span class="line"><span class="meta prompt_">pw-&gt;</span><span class="language-bash">pw_gecos = System Administrator</span></span><br><span class="line"><span class="meta prompt_">pw-&gt;</span><span class="language-bash">pw_dir = /var/root</span></span><br><span class="line"><span class="meta prompt_">pw-&gt;</span><span class="language-bash">pw_shell = /bin/sh</span></span><br></pre></td></tr></table></figure>
<p>发现uid、gid 均为 0。<br>于是在运行沙箱之前判定一下程序的执行者的uid、gid是不是 root 用户即可。</p>
<h2 id="资源限制"><a href="#资源限制" class="headerlink" title="资源限制"></a>资源限制</h2><h3 id="setuid-x2F-setgid"><a href="#setuid-x2F-setgid" class="headerlink" title="setuid&#x2F;setgid"></a>setuid&#x2F;setgid</h3><ul>
<li><p>background infomation<br>内核为每个进程维护的三个UID值。<br>这三个UID分别是实际用户ID(real uid)、有效用户ID(effective uid)、保存的设置用户ID(saved set-user ID)。<br>其中 real uid 指的是运行某程序的实际用户ID（登录shell的那个用户的uid）；<br>effective uid 指的是指当前进程是以哪个用户ID来运行的；<br>保存的设置用户ID就是有效用户ID的一个副本，与SUID权限有关。<br>一般情况下 real uid 和 effective uid 相同，但是使用<code>setuid</code>、<code>chmod +s</code>之后，二者就不一定相同了。</p>
</li>
<li><p><code>setuid</code>函数定义</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="type">int</span> <span class="title function_">setuid</span><span class="params">(<span class="type">uid_t</span> uid)</span>;</span><br></pre></td></tr></table></figure>
</li>
<li><p>函数说明：<br>+（1） 如果进程具有超级用户权限，那么 <code>setuid(uid_t uid)</code>会将三种 uid 全部设置成参数uid；<br> (启动sandbox其实就是要求以root身份启动的)</p>
<ul>
<li>(2) 如果 uid 等于 real uid 或者 saved set-user ID, 那么只把 effective uid 修改成 uid；</li>
<li>(3) 两种情况都不满组足，返回 -1 , errno被设置为 EPERM。</li>
</ul>
</li>
<li><p>返回值<br>执行成功则返回0； 失败则返回-1, 错误代码存于errno.</p>
</li>
<li><p>使用场景</p>
<ul>
<li>降低权限，比如在sandbox中通过让程序以 nobody 的身份来运行。</li>
<li>提高权限，但是最好注意在使用完 root 权限后建议马上执行setuid(getuid())，来抛弃root 权限，避免不必要的风险。</li>
</ul>
</li>
</ul>
<h3 id="setrlimit-x2F-getrlimit"><a href="#setrlimit-x2F-getrlimit" class="headerlink" title="setrlimit&#x2F;getrlimit"></a>setrlimit&#x2F;getrlimit</h3><ul>
<li><p>头文件:</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;sys/resource.h&gt;</span></span></span><br></pre></td></tr></table></figure>

</li>
<li><p>函数说明:<br>获取或设定资源使用限制。<br>每种资源都有相关的软硬限制:<strong>软限制</strong>是内核强加给相应资源的限制值，<strong>硬限制</strong>是软限制的最大值。<br>非授权调用进程只可以将其软限制指定为0~硬限制范围中的某个值，同时能不可逆转地降低其硬限制。<br>授权进程可以任意改变其软硬限制。<br>RLIM_INFINITY的值表示不对资源限制。</p>
</li>
<li><p>函数定义</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">getrlimit</span><span class="params">(<span class="type">int</span> resource, <span class="keyword">struct</span> rlimit *rlim)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">setrlimit</span><span class="params">(<span class="type">int</span> resource, <span class="type">const</span> <span class="keyword">struct</span> rlimit *rlim)</span>;</span><br></pre></td></tr></table></figure></li>
</ul>
<p>resource：可能的选择有</p>
<ul>
<li>RLIMIT_AS &#x2F;&#x2F; 进程的最大虚内存空间，字节为单位。</li>
<li>RLIMIT_CORE &#x2F;&#x2F; 内核转存文件的最大长度。</li>
<li>RLIMIT_CPU &#x2F;&#x2F; 最大允许的CPU使用时间，秒为单位。当进程达到软限制，内核将给其发送<strong>SIGXCPU</strong>信号，这一信号的默认行为是终止进程的执行。<br>然而，可以捕捉信号，处理句柄可将控制返回给主程序。<br>如果进程继续耗费CPU时间，核心会以每秒一次的频率给其发送SIGXCPU信号。<br>如果达到硬限制，那时将给进程发送 SIGKILL信号终止其执行。</li>
<li>RLIMIT_DATA &#x2F;&#x2F; 进程数据段的最大值。</li>
<li>RLIMIT_FSIZE &#x2F;&#x2F; 进程可建立的文件的最大长度。如果进程试图超出这一限制时，核心会给其发送<strong>SIGXFSZ</strong>信号，默认情况下将终止进程的执行。</li>
<li>RLIMIT_LOCKS &#x2F;&#x2F; 进程可建立的锁和租赁的最大值。</li>
<li>RLIMIT_MEMLOCK &#x2F;&#x2F; 进程可锁定在内存中的最大数据量，字节为单位。</li>
<li>RLIMIT_MSGQUEUE &#x2F;&#x2F; 进程可为POSIX消息队列分配的最大字节数。</li>
<li>RLIMIT_NICE &#x2F;&#x2F; 进程可通过setpriority() 或 nice()调用设置的最大完美值。</li>
<li>RLIMIT_NOFILE &#x2F;&#x2F; 指定比进程可打开的最大文件描述词大一的值，超出此值，将会产生EMFILE错误。</li>
<li>RLIMIT_NPROC &#x2F;&#x2F; 用户可拥有的最大进程数。</li>
<li>RLIMIT_RTPRIO &#x2F;&#x2F; 进程可通过sched_setscheduler 和 sched_setparam设置的最大实时优先级。</li>
<li>RLIMIT_SIGPENDING &#x2F;&#x2F; 用户可拥有的最大挂起信号数。</li>
<li>RLIMIT_STACK &#x2F;&#x2F; 最大的进程栈，以字节为单位。</li>
</ul>
<p>rlimit：描述资源软硬限制的结构体，原型如下</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">rlimit</span> &#123;</span></span><br><span class="line">	<span class="type">rlim_t</span>  rlim_cur;               <span class="comment">/* current (soft) limit 软限制 */</span></span><br><span class="line">	<span class="type">rlim_t</span>  rlim_max;               <span class="comment">/* maximum value for rlim_cur 硬限制 */</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<ul>
<li><p>返回值<br><strong>成功执行时，返回 0 。失败返回 -1</strong> 。<br>errno被设为以下的某个值：</p>
<ul>
<li>EFAULT：rlim指针指向的空间不可访问</li>
<li>EINVAL：参数无效</li>
<li>EPERM：增加资源限制值时，权能不允许</li>
</ul>
</li>
<li><p>参考链接：<br><a target="_blank" rel="noopener" href="https://man7.org/linux/man-pages/man2/getrlimit.2.html">getrlimit(2) — Linux manual page</a></p>
</li>
</ul>
<h1 id="使用seccomp限制syscall"><a href="#使用seccomp限制syscall" class="headerlink" title="使用seccomp限制syscall"></a>使用seccomp限制syscall</h1><h2 id="什么是seccomp"><a href="#什么是seccomp" class="headerlink" title="什么是seccomp"></a>什么是seccomp</h2><p>seccomp（全称 <strong>secure computing mode</strong>）是linux kernel从2.6.23版本开始所支持的一种安全机制。<br>seccomp是一种<strong>内核中的安全机制</strong>,正常情况下,程序可以使用所有的syscall,这是不安全的。<br>比如劫持程序流后通过execve的syscall来<code>getshell</code>。<br>通过seccomp我们可以在程序中禁用掉某些syscall,这样就算劫持了程序流也只能调用部分的syscall了.</p>
<p><strong>通过seccomp，我们限制程序使用某些系统调用，这样可以减少系统的暴露面，同时是程序进入一种“安全”的状态。</strong><br>详细介绍可参考seccomp内核文档(见参考链接)。</p>
<h2 id="如何使用seccomp"><a href="#如何使用seccomp" class="headerlink" title="如何使用seccomp"></a>如何使用seccomp</h2><p>seccomp可以通过系统调用ptrctl(2)或者通过系统调用seccomp(2)开启，前提是内核配置中开启了CONFIG_SECCOMP和CONFIG_SECCOMP_FILTER。</p>
<p>seccomp支持两种模式：<strong>SECCOMP_MODE_STRICT</strong> 和 <strong>SECCOMP_MODE_FILTER</strong>。</p>
<ul>
<li>在SECCOMP_MODE_STRICT模式下，进程不能使用<code>read(2)</code>、<code>write(2)</code>、<code>_exit(2)</code>和<code>sigreturn(2)</code>以外的其他系统调用。</li>
<li>在SECCOMP_MODE_FILTER模式下，可以利用BerkeleyPacket Filter配置哪些系统调用及它们的参数可以被进程使用。</li>
</ul>
<h2 id="如何查看是否使用了seccomp"><a href="#如何查看是否使用了seccomp" class="headerlink" title="如何查看是否使用了seccomp"></a>如何查看是否使用了seccomp</h2><p>通常有两种方法：<br>利用<code>prctl(2)</code>的PR_GET_SECCOMP的参数获取当前进程的seccomp状态。</p>
<ul>
<li>返回值0表示没有使用seccomp;</li>
<li>返回值2表示使用了seccomp并处于SECCOMP_MODE_FILTER模式； </li>
<li>其他情况进程会被SIGKILL信号杀死。</li>
</ul>
<p>从Linux3.8开始，可以利用&#x2F;proc&#x2F;$pid&#x2F;status中的Seccomp字段查看。如果没有seccomp字段，说明内核不支持seccomp。</p>
<ul>
<li><p>举例:<br>查看mysql服务的seccomp的状态，发现并没有进入安全限制模式。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">cat /proc/`pidof mysqld`/status</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">输出</span></span><br><span class="line">Name:	mysqld</span><br><span class="line">......</span><br><span class="line">Seccomp:	0</span><br><span class="line">Seccomp_filters:	0</span><br></pre></td></tr></table></figure>
<p>在sandbox环境下执行 python3 脚本，<br>查看次进程的seccomp的状态，发现进程处于SECCOMP_MODE_FILTER模式 。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Name:	python3</span><br><span class="line">......</span><br><span class="line">Seccomp:	2</span><br><span class="line">Seccomp_filters:	1</span><br></pre></td></tr></table></figure>
</li>
<li><p>代码示例<br>使用 <code>syscall</code> 调用 execve，如果没有安全限制的话，会正常进入 shell</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;sys/syscall.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;seccomp.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">main</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">    scmp_filter_ctx ctx; <span class="comment">// scmp 过滤上下文</span></span><br><span class="line">    ctx = seccomp_init(SCMP_ACT_ALLOW); <span class="comment">// 初始化过滤状态为允许所有系统调用</span></span><br><span class="line">    seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), <span class="number">0</span>); <span class="comment">// 添加需要限制的系统调用</span></span><br><span class="line">    seccomp_load(ctx); <span class="comment">// 装载上下文</span></span><br><span class="line"></span><br><span class="line">    <span class="type">char</span> *filename = <span class="string">&quot;/bin/sh&quot;</span>;</span><br><span class="line">    <span class="type">char</span> *argv[] = &#123;<span class="string">&quot;/bin/sh&quot;</span>, <span class="literal">NULL</span>&#125;;</span><br><span class="line">    <span class="type">char</span> *envp[] = &#123;<span class="literal">NULL</span>&#125;;</span><br><span class="line"></span><br><span class="line">    syscall(SYS_execve, filename, argv, envp); <span class="comment">// execve</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>编译</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">gcc -o ban ban.c -l seccomp</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">须先实现安装</span> </span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">sudo apt install libseccomp-dev libseccomp2 seccomp</span></span><br></pre></td></tr></table></figure>
<p>运行程序</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">songyangji@SongyangJi-Ubuntu-DeskStop:~/桌面$ ./ban</span><br><span class="line">错误的系统调用 (核心已转储) # Bad system call (core dumped)</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="api-rule"><a href="#api-rule" class="headerlink" title="api rule"></a>api rule</h2><h3 id="SCMP-SYS"><a href="#SCMP-SYS" class="headerlink" title="SCMP_SYS"></a>SCMP_SYS</h3><p>根据系统调用名获取系统调用号，虽然你可以直接使用 <code>__NR_syscall</code> 直接指定，但是为了跨平台最好使用它获取。</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">SCMP_SYS</span><span class="params">(syscall_name)</span>;</span><br></pre></td></tr></table></figure>

<h3 id="scmp-filter-ctx"><a href="#scmp-filter-ctx" class="headerlink" title="scmp_filter_ctx"></a>scmp_filter_ctx</h3><p>结构体定义</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">typedef</span> <span class="type">void</span> * scmp_filter_ctx;</span><br></pre></td></tr></table></figure>
<p>seccmp的过滤器上下文，保存、传递了我们传入的系统调用过滤条件。</p>
<h3 id="seccomp-init"><a href="#seccomp-init" class="headerlink" title="seccomp_init"></a>seccomp_init</h3><ul>
<li><p>函数说明:<br>seccomp_init的作用就是初始化 scmp_filter_ctx结构。<br>需要注意的是，任何其他libseccomp中的函数调用，必须在seccomp_init之后。</p>
</li>
<li><p>函数定义</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scmp_filter_ctx <span class="title function_">seccomp_init</span><span class="params">(<span class="type">uint32_t</span> def_action)</span>; </span><br></pre></td></tr></table></figure>
</li>
<li><p>返回值<br>成功返回scmp_filter_ctx（过滤器上下文） ctx；<br>失败返回NULL</p>
</li>
<li><p>参数说明<br>def_action用于指定默认行为，有效动作值如下：（当线程调用了<code>seccomp</code>过滤规则中没有相关配置规则的系统调用时触发）</p>
<ul>
<li>SCMP_ACT_KILL<br>线程将会被内核以SIGSYS信号终止；</li>
<li>SCMP_ACT_KILL_PROCESS<br>整个进程被终止；</li>
<li>SCMP_ACT_TRAP<br>线程将会抛出一个SIGSYS信号；</li>
<li>SCMP_ACT_ERRNO(uint16_t errno)<br>线程调用与筛选规则匹配的系统调用时，它将收到一个errno的返回值；</li>
<li>SCMP_ACT_TRACE(uint16_t msg_num)<br>略</li>
<li>SCMP_ACT_LOG<br>不会对调用系统调用的线程产生任何影响，但系统调用会被记录到日志。</li>
<li>SCMP_ACT_ALLOW<br>不会对调用系统调用的线程产生任何影响。</li>
</ul>
</li>
</ul>
<h3 id="seccomp-rule-addXXX"><a href="#seccomp-rule-addXXX" class="headerlink" title="seccomp_rule_addXXX"></a>seccomp_rule_addXXX</h3><h4 id="函数说明"><a href="#函数说明" class="headerlink" title="函数说明:"></a>函数说明:</h4><p>  这个函数组都会向当前seccomp过滤器添加新的过滤规则。</p>
<blockquote>
<p>调用应用程序提供的所有过滤器规则被组合成一个联合，并带有额外的逻辑来消除冗余的系统调用过滤器。<br>例如，如果添加了一条规则，该规则允许给定的系统调用具有一组特定的参数值，<br>然后又添加了一条规则，该规则允许相同的系统调用而不管参数值如何，<br>那么第一个更具体的规则将有效地从过滤器中删除第二个更通用的规则。</p>
</blockquote>
<h4 id="函数定义"><a href="#函数定义" class="headerlink" title="函数定义"></a>函数定义</h4><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">seccomp_rule_add</span><span class="params">(scmp_filter_ctx ctx, <span class="type">uint32_t</span> action,</span></span><br><span class="line"><span class="params">                            <span class="type">int</span> syscall, <span class="type">unsigned</span> <span class="type">int</span> arg_cnt, ...)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">seccomp_rule_add_exact</span><span class="params">(scmp_filter_ctx ctx, <span class="type">uint32_t</span> action,</span></span><br><span class="line"><span class="params">                                  <span class="type">int</span> syscall, <span class="type">unsigned</span> <span class="type">int</span> arg_cnt, ...)</span>;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">seccomp_rule_add_array</span><span class="params">(scmp_filter_ctx ctx,</span></span><br><span class="line"><span class="params">                                  <span class="type">uint32_t</span> action, <span class="type">int</span> syscall,</span></span><br><span class="line"><span class="params">                                  <span class="type">unsigned</span> <span class="type">int</span> arg_cnt,</span></span><br><span class="line"><span class="params">                                  <span class="type">const</span> <span class="keyword">struct</span> scmp_arg_cmp *arg_array)</span>;</span><br><span class="line"><span class="type">int</span> <span class="title function_">seccomp_rule_add_exact_array</span><span class="params">(scmp_filter_ctx ctx,</span></span><br><span class="line"><span class="params">                                        <span class="type">uint32_t</span> action, <span class="type">int</span> syscall,</span></span><br><span class="line"><span class="params">                                        <span class="type">unsigned</span> <span class="type">int</span> arg_cnt,</span></span><br><span class="line"><span class="params">                                        <span class="type">const</span> <span class="keyword">struct</span> scmp_arg_cmp *arg_array)</span>;</span><br></pre></td></tr></table></figure>


<h4 id="参数说明"><a href="#参数说明" class="headerlink" title="参数说明"></a>参数说明</h4><ul>
<li><ol>
<li>action有效动作值如下：（当线程调用了<code>seccomp</code>过滤规则中有相关配置规则的系统调用时触发）</li>
</ol>
<ul>
<li>SCMP_ACT_KILL<br>线程将会被内核终止；</li>
<li>SCMP_ACT_KILL_PROCESS<br>整个进程被终止；</li>
<li>SCMP_ACT_TRAP<br>线程将会抛出一个SIGSYS信号；</li>
<li>SCMP_ACT_ERRNO(uint16_t errno)<br>线程调用与筛选规则匹配的系统调用时，它将收到一个errno的返回值；</li>
<li>SCMP_ACT_TRACE(uint16_t msg_num)<br>略</li>
<li>SCMP_ACT_LOG<br>会对调用系统调用的线程产生任何影响，但系统调用会被记录到日志。</li>
<li>SCMP_ACT_ALLOW<br>不会对调用系统调用的线程产生任何影响（也就是允许调用这个system call）。</li>
<li>SCMP_ACT_NOTIFY<br>略</li>
</ul>
</li>
<li><ol start="2">
<li>arg_cnt 指定规则配置的系统调用的参数的匹配情况的个数（因为后面是一个变长数组）</li>
</ol>
</li>
<li><ol start="3">
<li>边长数组的元素是 <code>scmp_arg_cmp</code> 结构体，定义如下。</li>
</ol>
</li>
</ul>
<p>系统调用的参数比较规则相关定义：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Comparison operators</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">scmp_compare</span> &#123;</span></span><br><span class="line">	_SCMP_CMP_MIN = <span class="number">0</span>,</span><br><span class="line">	SCMP_CMP_NE = <span class="number">1</span>,		<span class="comment">/**&lt; not equal */</span></span><br><span class="line">	SCMP_CMP_LT = <span class="number">2</span>,		<span class="comment">/**&lt; less than */</span></span><br><span class="line">	SCMP_CMP_LE = <span class="number">3</span>,		<span class="comment">/**&lt; less than or equal */</span></span><br><span class="line">	SCMP_CMP_EQ = <span class="number">4</span>,		<span class="comment">/**&lt; equal */</span></span><br><span class="line">	SCMP_CMP_GE = <span class="number">5</span>,		<span class="comment">/**&lt; greater than or equal */</span></span><br><span class="line">	SCMP_CMP_GT = <span class="number">6</span>,		<span class="comment">/**&lt; greater than */</span></span><br><span class="line">	SCMP_CMP_MASKED_EQ = <span class="number">7</span>,		<span class="comment">/**&lt; masked equality */</span></span><br><span class="line">	_SCMP_CMP_MAX,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Argument datum</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="type">uint64_t</span> <span class="type">scmp_datum_t</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Argument / Value comparison definition</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">scmp_arg_cmp</span> &#123;</span></span><br><span class="line">	<span class="type">unsigned</span> <span class="type">int</span> arg;	<span class="comment">/**&lt; argument number, starting at 0 */</span></span><br><span class="line">	<span class="class"><span class="keyword">enum</span> <span class="title">scmp_compare</span> <span class="title">op</span>;</span>	<span class="comment">/**&lt; the comparison op, e.g. SCMP_CMP_* */</span></span><br><span class="line">	<span class="type">scmp_datum_t</span> datum_a;</span><br><span class="line">	<span class="type">scmp_datum_t</span> datum_b;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>有效比较操作值（ op ）如下：</p>
<ul>
<li><p>SCMP_CMP_NE<br>参数值不等于基准值时匹配，例如：<br>SCMP_CMP( arg , SCMP_CMP_NE , datum )</p>
</li>
<li><p>SCMP_CMP_LT<br>参数值小于基准值时匹配，例如：<br>SCMP_CMP( arg , SCMP_CMP_LT , datum )</p>
</li>
<li><p>SCMP_CMP_LE<br>参数值小于或等于基准值时匹配，例如：<br>SCMP_CMP( arg , SCMP_CMP_LE , datum )</p>
</li>
<li><p>SCMP_CMP_EQ<br>参数值等于基准值时匹配，例如：<br>SCMP_CMP( arg , SCMP_CMP_EQ , datum )</p>
</li>
<li><p>SCMP_CMP_GE<br>参数值大于或等于基准值时匹配，例如：<br>SCMP_CMP( arg , SCMP_CMP_GE , datum )</p>
</li>
<li><p>SCMP_CMP_GT<br>参数值大于基准值时匹配，例如：<br>SCMP_CMP( arg , SCMP_CMP_GT , datum )</p>
</li>
<li><p>SCMP_CMP_MASKED_EQ<br>当掩码参数值等于掩码基准值时匹配，例如：<br>SCMP_CMP( arg , SCMP_CMP_MASKED_EQ , mask , datum )</p>
</li>
</ul>
<p>注意，scmp_arg_cmp 此结构不能直接生成，需要调用它提供的宏生成，有如下宏：</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_CMP</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> arg,</span></span><br><span class="line"><span class="params">                                    <span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A0</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A1</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A2</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A3</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A4</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A5</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_CMP64</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> arg,</span></span><br><span class="line"><span class="params">                                    <span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A0_64</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A1_64</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A2_64</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A3_64</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A4_64</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A5_64</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line"></span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_CMP32</span><span class="params">(<span class="type">unsigned</span> <span class="type">int</span> arg,</span></span><br><span class="line"><span class="params">                                    <span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A0_32</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A1_32</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A2_32</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A3_32</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A4_32</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line">       <span class="keyword">struct</span> scmp_arg_cmp <span class="title function_">SCMP_A5_32</span><span class="params">(<span class="keyword">enum</span> scmp_compare op, ...)</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>解释一下上面的这么多宏的功能分类依据，A{0-5}中的0、1、2、3、4、5用于指定系统调用的那个参数，<br>32还是64自然是指定32位机器还是64位机器，<br>SCMP_CMP的第一个参数<code>unsigned int arg</code>的功能就是<code>A&#123;$arg_num&#125;</code>中的<code>$arg_num</code>，<br>所有宏的第一个参数<code>op</code>就是用于指定比较的规则，如上已经介绍过。</p>
<h4 id="返回值"><a href="#返回值" class="headerlink" title="返回值"></a>返回值</h4><p>函数成功时返回零；<br>失败时返回负的errno值。</p>
<h4 id="举例"><a href="#举例" class="headerlink" title="举例"></a>举例</h4><p>1. </p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), <span class="number">1</span>, SCMP_CMP(<span class="number">1</span>, SCMP_CMP_MASKED_EQ, O_WRONLY | O_RDWR, <span class="number">0</span>))</span><br></pre></td></tr></table></figure>
<p>指定<code>open(const *path, int oflags)</code>系统调用的<code>oflags</code>参数如果既没有O_WRONLY，也没有O_RDWR（二进制对应位），就是允许的，<br>换言之这条规则禁用掉了 open的 w、rw。</p>
<h3 id="seccomp-load"><a href="#seccomp-load" class="headerlink" title="seccomp_load"></a>seccomp_load</h3><ul>
<li><p>函数说明:<br>将ctx提供的seccomp过滤器加载到内核中；<br>如果函数成功，新的 seccomp 过滤器将在函数返回时处于活动状态</p>
</li>
<li><p>函数定义</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">seccomp_load</span><span class="params">(scmp_filter_ctx ctx)</span>;</span><br></pre></td></tr></table></figure>
</li>
<li><p>返回值<br>成功时返回0，失败时返回以下错误码：<br>-ECANCELED<br>  There was a system failure beyond the control of the<br>library.<br>-EFAULT<br>  Internal libseccomp failure.<br>-EINVAL<br>  Invalid input, either the context or architecture token is invalid.<br>-ENOMEM<br>  The library was unable to allocate enough memory.<br>-ESRCH<br>  Unable to load the filter due to thread issues.</p>
</li>
</ul>
<h3 id="seccomp-release-3"><a href="#seccomp-release-3" class="headerlink" title="seccomp_release(3)"></a>seccomp_release(3)</h3><ul>
<li><p>函数说明:<br>释放ctx 中的 seccomp 过滤器结构的内存，该过滤器首先由seccomp_init(3)或seccomp_reset(3)初始化，<br>并释放与给定 seccomp 过滤器上下文关联的任何内存。<br>加载到内核中的任何 seccomp过滤器不受影响。</p>
</li>
<li><p>函数定义</p>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">void</span> <span class="title function_">seccomp_release</span><span class="params">(scmp_filter_ctx ctx)</span>;</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h3><p>  <a target="_blank" rel="noopener" href="https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt">Secure Computing with filters</a><br>  <a target="_blank" rel="noopener" href="https://man7.org/linux/man-pages/man3/seccomp_init.3.html">seccomp_init(3)</a><br>  <a target="_blank" rel="noopener" href="https://man7.org/linux/man-pages/man3/seccomp_rule_add.3.html">seccomp_rule_add(3)</a><br>  <a target="_blank" rel="noopener" href="https://man7.org/linux/man-pages/man3/seccomp_load.3.html">seccomp_load(3)</a><br>  <a target="_blank" rel="noopener" href="https://man7.org/linux/man-pages/man3/seccomp_release.3.html">seccomp_release(3)</a></p>
<blockquote>
<p>参考链接</p>
<p><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E6%B2%99%E7%9B%92_(%E9%9B%BB%E8%85%A6%E5%AE%89%E5%85%A8)">沙箱安全</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/10/LinkLayer/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/10/LinkLayer/" class="post-title-link" itemprop="url">LinkLayer</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-10 20:53:24" itemprop="dateCreated datePublished" datetime="2021-12-10T20:53:24+08:00">2021-12-10</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/10/IP%20%E5%8D%8F%E8%AE%AE%E7%9B%B8%E5%85%B3%E6%8A%80%E6%9C%AF%E2%80%94%E2%80%94DNS%E3%80%81NAT%E3%80%81ICMP%E3%80%81DHCP%E3%80%81ARP/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/10/IP%20%E5%8D%8F%E8%AE%AE%E7%9B%B8%E5%85%B3%E6%8A%80%E6%9C%AF%E2%80%94%E2%80%94DNS%E3%80%81NAT%E3%80%81ICMP%E3%80%81DHCP%E3%80%81ARP/" class="post-title-link" itemprop="url">IP 协议相关技术——DNS、NAT、ICMP、DHCP、ARP</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-10 00:42:13" itemprop="dateCreated datePublished" datetime="2021-12-10T00:42:13+08:00">2021-12-10</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-03-16 17:13:07" itemprop="dateModified" datetime="2022-03-16T17:13:07+08:00">2022-03-16</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/" itemprop="url" rel="index"><span itemprop="name">计算机网络</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="DNS"><a href="#DNS" class="headerlink" title="DNS"></a>DNS</h1><blockquote>
<p>注意DNS是应用层的协议，不是网络层的协议。</p>
</blockquote>
<h2 id="DNS简介"><a href="#DNS简介" class="headerlink" title="DNS简介"></a>DNS简介</h2><p>DNS（Domain Name System）是域名系统的英文缩写，是一种组织成域层次结构的计算机和网络服务命名系统，用于 TCP&#x2F;IP 网络。</p>
<h2 id="DNS-作用"><a href="#DNS-作用" class="headerlink" title="DNS 作用"></a>DNS 作用</h2><p>通常我们有两种方式识别主机：通过主机名或者 IP 地址。人们喜欢便于记忆的主机名表示，而路由器则喜欢定长的、有着层次结构的 IP 地址。为了满足这些不同的偏好，我们就需要一种能够进行主机名到 IP 地址转换的目录服务，<strong>域名系统作为将域名和 IP 地址相互映射的一个分布式数据库</strong>，能够使人更方便地访问互联网。</p>
<h2 id="DNS-域名解析原理"><a href="#DNS-域名解析原理" class="headerlink" title="DNS 域名解析原理"></a>DNS 域名解析原理</h2><p>DNS 采用了分布式的设计方案，其域名空间采用一种树形的层次结构：</p>
<p><img src="/2021/12/10/IP%20%E5%8D%8F%E8%AE%AE%E7%9B%B8%E5%85%B3%E6%8A%80%E6%9C%AF%E2%80%94%E2%80%94DNS%E3%80%81NAT%E3%80%81ICMP%E3%80%81DHCP%E3%80%81ARP/dns.png"></p>
<p>上图展示了 DNS 服务器的部分层次结构，从上到下依次为<strong>根域名服务器</strong>、<strong>顶级域名服务器</strong>和<strong>权威域名服务器</strong>。</p>
<ol>
<li><p>域名服务器在因特网上有13个，大部分位于北美洲。</p>
</li>
<li><p>第二层为顶级域服务器，这些服务器负责顶级域名（如 com、org、net、edu）和所有国家的顶级域名（如uk、fr、ca 和 jp）。</p>
</li>
<li><p>在第三层为权威 DNS 服务器，因特网上具有公共可访问主机（例如 Web 服务器和邮件服务器）的每个组织机构必须提供公共可访问的 DNS 记录，这些记录由组织机构的权威 DNS 服务器负责保存，这些记录将这些主机的名称映射为 IP 地址。</p>
</li>
</ol>
<p>所以域名的层级关系类似⼀个树状结构：</p>
<ul>
<li><p>根 DNS 服务器</p>
</li>
<li><p>顶级域 DNS 服务器（com）</p>
</li>
<li><p>权威 DNS 服务器（server.com）</p>
</li>
</ul>
<p>除此之外，还有一类重要的 DNS 服务器，叫做<strong>本地 DNS 服务器</strong>。本地 DNS 服务器严格来说不在 DNS 服务器的层次结构中，但它对 DNS 层次结构是很重要的。</p>
<p>一般来说，<strong>每个网络服务提供商（ISP） 都有一台本地 DNS 服务器</strong>。<strong>当主机与某个 ISP 相连时，该 ISP 提供一台主机的 IP 地址，该主机具有一台或多台其本地 DNS 服务器的 IP 地址</strong>。主机的本地 DNS 服务器通常和主机距离较近，<strong>当主机发起 DNS 请求时，该请求被发送到本地 DNS 服务器，它起着代理的作用，并将该请求转发到 DNS 服务器层次结构中</strong>。</p>
<h2 id="举例"><a href="#举例" class="headerlink" title="举例"></a>举例</h2><img src="dns2.png" style="zoom:30%;" />

<p>我们以一个例子来了解 DNS 的工作原理，假设主机 A（IP 地址为 abc.xyz.edu） 想知道主机 B 的 IP 地址 （def.mn.edu）。</p>
<ol>
<li>主机 A 首先<strong>向它的本地 DNS 服务器发送一个 DNS 查询报文</strong>。该查询报文含有被转换的主机名 def.mn.edu</li>
<li>本地 DNS 服务器<strong>将该报文转发到根 DNS 服务器</strong>；</li>
<li>根 DNS 服务器注意到查询的 IP 地址前缀为 edu 后向本地 DNS 服务器<strong>返回负责 edu 的顶级域名服务器的 IP 地址列表</strong>。</li>
<li>该本地 DNS 服务器则再次向这些顶级域名服务器发送查询报文。该顶级域名服务器注意到 mn.edu 的前缀，并<strong>用权威域名服务器的 IP 地址进行响应</strong>。</li>
<li>通常情况下，顶级域名服务器并不总是知道每台主机的权威 DNS 服务器的 IP 地址，而只知道中间的某个服务器，该中间 DNS 服务器依次能找到用于相应主机的 IP 地址，我们假设中间经历了权威服务器 ① 和 ②，最后<strong>找到了负责 def.mn.edu 的权威 DNS 服务器</strong> ③</li>
<li>之后，<strong>本地 DNS 服务器直接向该服务器发送查询报文</strong>从而获得主机 B 的IP 地址。</li>
</ol>
<p>在上图中，IP 地址的查询其实经历了两种查询方式，分别是递归查询和迭代查询。</p>
<p><strong>域名解析查询的两种方式</strong></p>
<p>DNS查询有两种方式：<strong>递归</strong>和<strong>迭代</strong>。</p>
<ul>
<li><p>DNS客户端设置使用的DNS服务器一般都是递归服务器，它负责全权处理客户端的DNS查询请求，直到返回最终结果。</p>
</li>
<li><p>DNS服务器之间一般采用迭代查询方式。</p>
</li>
</ul>
<h2 id="DNS-为什么用-UDP"><a href="#DNS-为什么用-UDP" class="headerlink" title="DNS 为什么用 UDP"></a>DNS 为什么用 UDP</h2><h3 id="综合使用TCP和UDP"><a href="#综合使用TCP和UDP" class="headerlink" title="综合使用TCP和UDP"></a>综合使用TCP和UDP</h3><p>更正确的答案是 DNS 既使用 TCP 又使用 UDP。</p>
<p>当进行区域传送（<strong>主域名服务器向辅助域名服务器传送变化的那部分数据</strong>）时会使用 TCP，因为数据同步传送的数据量比一个请求和应答的数据量要多，而 TCP 允许的报文长度更长，因此为了保证数据的正确性，会使用基于可靠连接的 TCP。（DNS服务器与服务器之间用TCP）</p>
<p>udp适用于小包，无上下文的情况，好处是开销小。很容易理解，udp查询就是一问一答，url和ip地址都是很短的字符串，都不会超过一个包。</p>
<p>当客户端向 DNS 服务器查询域名 ( 域名解析) 的时候，一般返回的内容不会超过 <strong>DNS 报文的最大长度，即 512 字节</strong>。用 UDP 传输时，不需要经过 TCP 三次握手的过程，从而大大提高了响应速度，但这要求域名解析器和域名服务器都必须自己处理超时和重传从而保证可靠性。</p>
<h3 id="历史发展"><a href="#历史发展" class="headerlink" title="历史发展"></a>历史发展</h3><p>当年内容贫乏，硬件性能低下。认为主机查询的动作频次低，数据量少。用TCP短连结握手和挥手的开销比查询还高。用长连接服务器又承受不住。UDP是很好的选择。</p>
<p>互联网起来后DNS频率急剧增加，所以又加上了TCP版本。</p>
<p>随着恶意的DNS污染出现后，DNS又升级了TLS版本。</p>
<h3 id="无需TCP的可靠传输（省去握手挥手开销）"><a href="#无需TCP的可靠传输（省去握手挥手开销）" class="headerlink" title="无需TCP的可靠传输（省去握手挥手开销）"></a>无需TCP的可靠传输（省去握手挥手开销）</h3><ol>
<li>第一，传输包的大小。首先我们知道tcp要做的事很简单，就是要保证一个可靠及高效的服务。所谓可靠，那就是比如说，本要发的包丢了，那就重传，目的尽量保证这个包到达对端。但是你如果包很大，你每次都整体重传，那肯定不满足高效的原则了（tcp做的很大一部分工作也就是把需要丢的包重传而不是每次整体重传）。基于此，dns查询的包基本大小就是固定的，就是一个包的大小，如果丢了那就再传一次就得了，根本不需要tcp的这种可靠传输。</li>
<li>第二，如果只是传输一个包大小，再来几个握手来几个挥手，就太得不偿失了。</li>
<li>第三，dns查询，本身一个查询，对端就会给一个回复，这个回复就可以当做是一个响应。已经能满足dns查询的这种需求了。</li>
</ol>
<p><a target="_blank" rel="noopener" href="https://segmentfault.com/a/1190000039759438"><a target="_blank" rel="noopener" href="https://segmentfault.com/a/1190000039759438">DNS劫持如何避免和解决?</a></a></p>
<h1 id="ARP-地址解析协议"><a href="#ARP-地址解析协议" class="headerlink" title="ARP 地址解析协议"></a>ARP 地址解析协议</h1><p>地址解析协议（英语：Address Resolution Protocol，缩写：ARP）是一个通过解析网络层地址来找寻数据链路层地址的网络传输协议，它在IPv4中极其重要。</p>
<p>在IPv6中<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E9%82%BB%E5%B1%85%E5%8F%91%E7%8E%B0%E5%8D%8F%E8%AE%AE">邻居发现协议</a>（NDP）用于代替地址解析协议（ARP）。</p>
<h2 id="基本功能"><a href="#基本功能" class="headerlink" title="基本功能"></a>基本功能</h2><p>在<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E4%BB%A5%E5%A4%AA%E7%BD%91">以太网</a>协议中规定，<strong>同一局域网中的一台主机要和另一台主机进行直接通信，必须要知道目标主机的MAC地址</strong>。而在TCP&#x2F;IP协议中，网络层和传输层只关心目标主机的IP地址。</p>
<p>这就导致在以太网中使用IP协议时，数据链路层的以太网协议接到上层IP协议提供的数据中，只包含目的主机的IP地址。于是需要一种方法，<strong>根据目的主机的IP地址，获得其<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/MAC%E5%9C%B0%E5%9D%80">MAC地址</a>。这就是ARP协议要做的事情</strong>。所谓<strong>地址解析（address resolution）</strong>就是主机在发送帧前将目标IP地址转换成目标MAC地址的过程。</p>
<p>另外，当发送主机和目的主机不在同一个<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E5%B1%80%E5%9F%9F%E7%BD%91">局域网</a>中时，即便知道对方的MAC地址，两者也不能直接通信，必须经过<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E8%B7%AF%E7%94%B1">路由</a>转发才可以。所以<strong>此时，发送主机通过ARP协议获得的将不是目的主机的真实MAC地址，而是一台可以通往局域网外的路由器的MAC地址</strong>。于是此后发送主机发往目的主机的所有帧，都将发往该路由器，通过它向外发送。这种情况称为委托ARP或<strong>ARP代理（ARP Proxy）</strong>。</p>
<p>在<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E7%82%B9%E5%AF%B9%E7%82%B9%E5%8D%8F%E8%AE%AE">点对点链路</a>中不使用ARP，实际上在<strong>点对点网络</strong>中也不使用MAC地址，因为在此类网络中分别已经获取了对端的IP地址。</p>
<h2 id="数据包结构"><a href="#数据包结构" class="headerlink" title="数据包结构"></a>数据包结构</h2><p>为了把IP地址映射到48位以太网地址用于传输，需要一个体现地址转换协议的包格式。</p>
<h3 id="以太网链路层"><a href="#以太网链路层" class="headerlink" title="以太网链路层"></a>以太网链路层</h3><ul>
<li>目标以太网地址：目标MAC地址。FF:FF:FF:FF:FF:FF （二进制全1）为广播地址。</li>
<li>源以太网地址：发送方MAC地址。</li>
<li>帧类型：以太类型，ARP为0x0806。</li>
</ul>
<h3 id="以太网报文数据"><a href="#以太网报文数据" class="headerlink" title="以太网报文数据"></a>以太网报文数据</h3><ul>
<li>硬件类型：如以太网（0x0001）、<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E5%B0%81%E5%8C%85%E7%84%A1%E7%B7%9A%E9%9B%BB">分组无线网</a>。</li>
<li>协议类型：如<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E7%BD%91%E9%99%85%E5%8D%8F%E8%AE%AE">网际协议</a>(IP)（0x0800）、<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/IPv6">IPv6</a>（0x86DD）。</li>
<li>硬件地址长度：每种硬件地址的字节长度，一般为6（以太网）。</li>
<li>协议地址长度：每种协议地址的字节长度，一般为4（IPv4）。</li>
<li>操作码：1为ARP请求，2为ARP应答，3为<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E9%80%86%E5%9C%B0%E5%9D%80%E8%A7%A3%E6%9E%90%E5%8D%8F%E8%AE%AE">RARP</a>请求，4为RARP应答。</li>
<li><strong>源硬件地址</strong>：n个字节，n由硬件地址长度得到，一般为发送方MAC地址。</li>
<li><strong>源协议地址</strong>：m个字节，m由协议地址长度得到，一般为发送方IP地址。</li>
<li><strong>目标硬件地址</strong>：n个字节，n由硬件地址长度得到，一般为目标MAC地址。</li>
<li><strong>目标协议地址</strong>：m个字节，m由协议地址长度得到，一般为目标IP地址。</li>
</ul>
<table class="wikitable" style="text-align:center;">
<tbody><tr>
<th>长度(位)</th>
<th>48</th>
<th>48</th>
<th>16</th>
<th>16</th>
<th>16</th>
<th>8</th>
<th>8</th>
<th>16</th>
<th>48</th>
<th>32</th>
<th>48</th>
<th>32
</th></tr>
<tr>
<td style="min-height:60px;"><b>数据类型</b></td>
<td style="width:96px;">目标以太网地址</td>
<td style="width:96px;">源以太网地址</td>
<td style="width:32px;">帧类型</td>
<td style="width:32px;">硬件类型</td>
<td style="width:32px;">协议类型</td>
<td style="width:16px;">硬件地址长度</td>
<td style="width:16px;">协议地址长度</td>
<td style="width:32px;">操作码</td>
<td style="width:96px;">源硬件地址</td>
<td style="width:64px;">源协议地址</td>
<td style="width:96px;">目标硬件地址</td>
<td style="width:64px;">目标协议地址
</td></tr>
<tr>
<td><b>组成</b></td>
<td colspan="3">14字节 以太网首部</td>
<td colspan="9">28字节 ARP请求/应答
</td></tr></tbody></table>


<h2 id="实现原理"><a href="#实现原理" class="headerlink" title="实现原理"></a>实现原理</h2><p>在每台安装有TCP&#x2F;IP协议的电脑或路由器里都有一个<strong>ARP缓存表，表里的IP地址与MAC地址是一对应的</strong>。</p>
<p>以主机A（192.168.38.10）向主机B（192.168.38.11）发送数据为例。</p>
<ol>
<li>当发送数据时，主机A会<strong>先在自己的ARP缓存表中寻找是否有目标IP地址</strong>。如果找到就知道目标MAC地址为（00-BB-00-62-C2-02），直接把目标MAC地址写入帧里面发送就可。</li>
<li>如果在ARP缓存表中没有找到相对应的IP地址，<strong>主机A就会在网络上发送一个广播（ARP request），目标MAC地址是“FF.FF.FF.FF.FF.FF”</strong>，这表示向同一网段内的所有主机发出这样的询问：“192.168.38.11的MAC地址是什么？”</li>
<li><strong>网络上其他主机并不响应ARP询问，只有主机B接收到这个帧时，才向主机A做出这样的回应</strong>（ARP response）：“192.168.38.11的MAC地址是00-BB-00-62-C2-02”，此回应以单播方式。这样，主机A就知道主机B的MAC地址，它就可以向主机B发送信息。同时它<strong>还更新自己的ARP高速缓存</strong>（ARP cache），下次再向主机B发送信息时，直接从ARP缓存表里查找就可。</li>
</ol>
<p>ARP缓存表采用老化机制，在一段时间内如果表中的某一行没有使用，就会被删除，这样可减少缓存表的长度，加快查询速度。</p>
<h2 id="免费ARP（gratuitous-ARP）"><a href="#免费ARP（gratuitous-ARP）" class="headerlink" title="免费ARP（gratuitous ARP）"></a>免费ARP（gratuitous ARP）</h2><p>主机发送ARP查询（广播）自己的IP地址，当ARP功能被开启或者是端口初始配置完成，主机向网络发送免费ARP来<strong>查询自己的IP地址确认地址唯一可用</strong>。</p>
<p>作用：</p>
<ol>
<li><strong>确定网络中是否有其他主机使用了IP地址</strong>，如果有应答则产生错误消息。</li>
<li><strong>免费ARP可以做更新ARP缓存用</strong>，网络中的其他主机收到该广播则在缓存中更新条目，收到该广播的主机无论是否存在与IP地址相关的条目都会强制更新，如果存在旧条目则会将MAC更新为广播包中的MAC。</li>
</ol>
<blockquote>
<p>也就是主动告诉其他主机自己的IP和Mac地址的对应关系，以供更新之用。</p>
</blockquote>
<blockquote>
<p>参考<br><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E5%9C%B0%E5%9D%80%E8%A7%A3%E6%9E%90%E5%8D%8%E8%AE%AE">ARP</a></p>
</blockquote>
<h1 id="NAT（网络地址转换）"><a href="#NAT（网络地址转换）" class="headerlink" title="NAT（网络地址转换）"></a>NAT（网络地址转换）</h1><p>NAT（Network Address Translation），即<strong>网络地址转换</strong>，它是一种把内部私有网络地址翻译成公有网络 IP 地址的技术。该技术不仅能解决 IP 地址不足的问题，而且还能隐藏和保护网络内部主机，从而避免来自外部网络的攻击。</p>
<p>NAT 的实现方式主要有三种：</p>
<ul>
<li>静态转换（Basic NAT）：<strong>内部私有 IP 地址和公有 IP 地址是一对一的关系</strong>，并且不会发生改变。通过静态转换，可以实现外部网络对内部网络特定设备的访问，这种方式原理简单，但当某一共有 IP 地址被占用时，跟这个 IP 绑定的内部主机将无法访问 Internet。</li>
<li>动态转换：采用动态转换的方式时，<strong>私有 IP 地址每次转化成的公有 IP 地址是不唯一的</strong>。当私有 IP 地址被授权访问 Internet 时会被随机转换成一个合法的公有 IP 地址。当 ISP 通过的合法 IP 地址数量略少于网络内部计算机数量时，可以采用这种方式。</li>
<li><strong>端口多路复用</strong>（NAPT）：该方式将外出数据包的源端口进行端口转换，<strong>通过端口多路复用的方式，实现内部网络所有主机共享一个合法的外部 IP 地址进行 Internet 访问</strong>，从而最大限度地节约 IP 地址资源。同时，该方案可以隐藏内部网络中的主机，从而有效避免来自 Internet 的攻击。</li>
</ul>
<p>NAPT维护一个带有IP以及端口号的NAT表，结构如下。</p>
<table>
<thead>
<tr>
<th align="center">内网IP</th>
<th align="center">外网IP</th>
</tr>
</thead>
<tbody><tr>
<td align="center">192.168.1.55:5566</td>
<td align="center">219.152.168.222:9200</td>
</tr>
<tr>
<td align="center">192.168.1.59:80</td>
<td align="center">219.152.168.222:9201</td>
</tr>
<tr>
<td align="center">192.168.1.59:4465</td>
<td align="center">219.152.168.222:9202</td>
</tr>
</tbody></table>
<p>支持端口转换的NAT可以分为两类：<strong>源地址转换</strong>和<strong>目的地址转换</strong>。</p>
<p>前一种情形下发起连接的计算机的IP地址将会被重写，使得内网主机发出的数据包能够到达外网主机。</p>
<p>后一种情况下被连接计算机的IP地址将被重写，使得外网主机发出的数据包能够到达内网主机。</p>
<p>实际上，<strong>以上两种方式通常会一起被使用以支持双向通信</strong>。</p>
<p>如何支持这样的一对多的映射关系呢？就是利用发送端的源端口来进行的。</p>
<h1 id="DHCP"><a href="#DHCP" class="headerlink" title="DHCP"></a>DHCP</h1><p><strong>动态主配置协议</strong>（英语：<strong>D</strong>ynamic <strong>H</strong>ost <strong>C</strong>onfiguration <strong>P</strong>rotocol，缩写：<strong>DHCP</strong>），是一个用于<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E7%BD%91%E9%99%85%E5%8D%8F%E8%AE%AE">IP</a>网络的<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE">网络协议</a>，位于<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B">OSI模型</a>的<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E5%BA%94%E7%94%A8%E5%B1%82">应用层</a>，使用<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E7%94%A8%E6%88%B7%E6%95%B0%E6%8D%AE%E6%8A%A5%E5%8D%8F%E8%AE%AE">UDP</a>协议工作，主要有两个用途：</p>
<ul>
<li><strong>用于内部网或网络服务供应商自动分配IP地址给用户</strong>；</li>
<li>用于内部网管理员对所有电脑作中央管理。</li>
</ul>
<blockquote>
<p> 我们的电脑通常都是通过 DHCP 动态获取 IP 地址，⼤⼤省去了配 IP 信息繁琐的过程。</p>
</blockquote>
<h2 id="DHCP工作步骤"><a href="#DHCP工作步骤" class="headerlink" title="DHCP工作步骤"></a>DHCP工作步骤</h2><ol>
<li><strong>发现</strong> ：客户端⾸先发起 <strong>DHCP</strong> 发现报⽂（<strong>DHCP DISCOVER</strong>） 的 IP 数据报，由于客户端没有 IP 地址，也不知道</li>
</ol>
<p>DHCP 服务器的地址，所以使⽤的是 UDP ⼴播通信，其使⽤的⼴播⽬的地址是 255.255.255.255（端⼝</p>
<p>67） 并且使⽤ 0.0.0.0（端⼝ 68） 作为源 IP 地址。DHCP 客户端将该 IP 数据报传递给链路层，链路层然后</p>
<p>将帧⼴播到所有的⽹络中设备。</p>
<ol start="2">
<li><p><strong>提供</strong> ：DHCP 服务器收到 DHCP 发现报⽂时，⽤ <strong>DHCP</strong> 提供报⽂（<strong>DHCP OFFER</strong>） 向客户端做出响应。该报⽂仍然使⽤ IP ⼴播地址 255.255.255.255，该报⽂信息携带服务器提供可租约的 IP 地址、⼦⽹掩码、默认⽹关、DNS 服务器以及 <strong>IP</strong> 地址租⽤期。</p>
</li>
<li><p><strong>请求</strong> ：客户端收到⼀个或多个服务器的 DHCP 提供报⽂后，从中选择⼀个服务器，并向选中的服务器发送 <strong>DHCP</strong> 请求报⽂（<strong>DHCP REQUEST</strong>进⾏响应，回显配置的参数。</p>
</li>
<li><p><strong>确认</strong> ：最后，服务端⽤ <strong>DHCP ACK</strong> 报⽂对 DHCP 请求报⽂进⾏响应，应答所要求的参数。</p>
</li>
</ol>
<p>⼀旦客户端收到 DHCP ACK 后，交互便完成了，并且<strong>客户端能够在租⽤期内使⽤ DHCP 服务器分配的 IP 地址</strong>。</p>
<p><strong>如果租约的 DHCP IP 地址快到期时，客户端会向服务器发送 DHCP 请求报⽂</strong>：</p>
<ul>
<li><p>服务器如果同意继续租⽤，则⽤ DHCP ACK 报⽂进⾏应答，客户端就会延⻓租期。</p>
</li>
<li><p>服务器如果不同意继续租⽤，则⽤ DHCP NACK 报⽂，客户端就要停⽌使⽤租约的 IP 地址。</p>
</li>
</ul>
<p>可以发现，DHCP 交互中，全程都是使⽤ <strong>UDP</strong> ⼴播通信。</p>
<h2 id="DHCP中继"><a href="#DHCP中继" class="headerlink" title="DHCP中继"></a>DHCP中继</h2><blockquote>
<p> 如果 DHCP 服务器和客户端不是在同⼀个局域⽹内，路由器⼜不会转发⼴播包，那不是每个⽹络都要配⼀个 DHCP 服务器？</p>
</blockquote>
<img src="dhcp.png" style="zoom:60%;" />



<p>所以，为了解决这⼀问题，就出现了 <strong>DHCP中继代理</strong>。有了 DHCP 中继代理以后，对不同⽹段的 <strong>IP</strong> 地址分配也可以由⼀个 <strong>DHCP</strong> 服务器统⼀进⾏管理。DHCP 客户端会向 DHCP 中继代理发送 DHCP 请求包，⽽ <strong>DHCP 中继代理在收到这个⼴播包以后，再以单播的形式发给 DHCP 服务器</strong>。</p>
<p>服务器端收到该包以后再向 DHCP 中继代理返回应答，并由 DHCP 中继代理将此包⼴播给 DHCP 客户端 。</p>
<p>因此，DHCP 服务器即使不在同⼀个链路上也可以实现统⼀分配和管理IP地址。</p>
<h1 id="ICMP"><a href="#ICMP" class="headerlink" title="ICMP"></a>ICMP</h1><p><strong>ICMP</strong>（Internet Control Message Protocol）是<strong>因特网控制报文协议</strong>，主要是实现 IP 协议中未实现的部分功能，是一种网络层协议。</p>
<p><strong>该协议并不传输数据，只传输控制信息来辅助网络层通信</strong>。</p>
<p>路由器严密监视Internet的操作，当路由器处理一个数据包发生意外的时候，可通过Internet控制消息协议向数据包的源端报告有关事件。</p>
<p>其主要的功能是验证网络是否畅通（确认接收方是否成功接收到 IP 数据包）以及辅助 IP 协议实现可靠传输（若发生 IP 丢包，ICMP 会通知发送方 IP 数据包被丢弃的原因，之后发送方会进行相应的处理）。</p>
<p>最重要的消息类型：</p>
<ol>
<li>目的地不可达：数据包无法传递，原因如不能定位一个目标，或者设置了DF但却遇到了小数据包网络；</li>
<li>超时：当TTL达到0而被丢弃时，路由器发送超时消息；</li>
<li>参数问题：在头字段中检测到一个非法字段；</li>
<li>源抑制：抑制包，可以用于拥塞控制（但现在很少使用）；</li>
<li>重定向：一个数据包被错误的路由；</li>
<li>响应请求与应答：检查一台机器是否存活；</li>
<li>请求&#x2F;应答时间戳：与上面类似不过要求带上时间戳。</li>
</ol>
<h2 id="ICMP应用"><a href="#ICMP应用" class="headerlink" title="ICMP应用"></a>ICMP应用</h2><ul>
<li><p>Ping<br>Ping（Packet Internet Groper），即<strong>因特网包探测器</strong>，是一种工作在网络层的服务命令，主要用于测试网络连接量。本地主机通过向目的主机发送 ICMP Echo 请求报文，目的主机收到之后会发送 Echo 响应报文，Ping 会根据时间和成功响应的次数估算出数据包往返时间以及丢包率从而推断网络是否通畅、运行是否正常等。</p>
</li>
<li><p>TraceRoute<br>TraceRoute 是 ICMP 的另一个应用，其<strong>主要用来跟踪一个分组从源点耗费最少 TTL 到达目的地的路径</strong>。TraceRoute 通过逐渐增大 TTL 值并重复发送数据报来实现其功能，首先，TraceRoute 会发送一个 TTL 为 1 的 IP 数据报到目的地，当路径上的第一个路由器收到这个数据报时，它将 TTL 的值减 1，此时 TTL &#x3D; 0，所以路由器会将这个数据报丢掉，并返回一个差错报告报文，之后源主机会接着发送一个 TTL 为 2 的数据报，并重复此过程，直到数据报能够刚好到达目的主机。此时 TTL &#x3D; 0，因此目的主机要向源主机发送 ICMP 终点不可达差错报告报文，之后源主机便知道了到达目的主机所经过的路由器 IP 地址以及到达每个路由器的往返时间。</p>
</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/09/Redis%E9%AB%98%E5%8F%AF%E7%94%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/09/Redis%E9%AB%98%E5%8F%AF%E7%94%A8/" class="post-title-link" itemprop="url">Redis高可用</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-09 20:45:27" itemprop="dateCreated datePublished" datetime="2021-12-09T20:45:27+08:00">2021-12-09</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-10 21:56:33" itemprop="dateModified" datetime="2021-12-10T21:56:33+08:00">2021-12-10</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>在介绍哨兵之前，首先从宏观角度回顾一下Redis实现高可用相关的技术。它们包括：持久化、复制、哨兵和集群，其主要作用和解决的问题是：</p>
<ul>
<li>持久化：持久化是最简单的高可用方法(有时甚至不被归为高可用的手段)，主要作用是数据备份，即将数据存储在硬盘，保证数据不会因进程退出而丢失。</li>
<li>复制：复制是高可用Redis的基础，哨兵和集群都是在复制基础上实现高可用的。复制主要实现了数据的多机备份，以及对于读操作的负载均衡和简单的故障恢复。缺陷：故障恢复无法自动化；写操作无法负载均衡；存储能力受到单机的限制。</li>
<li>哨兵：在复制的基础上，哨兵实现了自动化的故障恢复。缺陷：写操作无法负载均衡；存储能力受到单机的限制。</li>
<li>集群：通过集群，Redis解决了写操作无法负载均衡，以及存储能力受到单机限制的问题，实现了较为完善的高可用方案。</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" title="上一页" aria-label="上一页" href="/page/14/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/14/">14</a><span class="page-number current">15</span><a class="page-number" href="/page/16/">16</a><span class="space">&hellip;</span><a class="page-number" href="/page/26/">26</a><a class="extend next" rel="next" title="下一页" aria-label="下一页" href="/page/16/"><i class="fa fa-angle-right"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SongyangJi</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>





  





</body>
</html>
